Merge changes I2ea61b94,Ia7f45701 into androidx-main

* changes:
  Add executable tests to compiler
  Move composition test setup into a separate package.
diff --git a/activity/activity/src/androidTest/AndroidManifest.xml b/activity/activity/src/androidTest/AndroidManifest.xml
index e7c2c54..74f9f6f 100644
--- a/activity/activity/src/androidTest/AndroidManifest.xml
+++ b/activity/activity/src/androidTest/AndroidManifest.xml
@@ -31,6 +31,9 @@
             android:name="androidx.activity.LazyOverrideLifecycleComponentActivity"
             android:exported="true" />
         <activity
+            android:name="androidx.activity.EarlyDispatcherAccessComponentActivity"
+            android:exported="true" />
+        <activity
             android:name="androidx.activity.ComponentActivity"
             android:exported="true" />
         <activity
diff --git a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityCallbacksTest.kt b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityCallbacksTest.kt
index d0bdd8d..1c67712 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityCallbacksTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityCallbacksTest.kt
@@ -105,8 +105,8 @@
             val expectedFontScale = receivedFontScale * 2
 
             val listener = object : Consumer<Configuration> {
-                override fun accept(newConfig: Configuration) {
-                    receivedFontScale = newConfig.fontScale
+                override fun accept(value: Configuration) {
+                    receivedFontScale = value.fontScale
                     activity.removeOnConfigurationChangedListener(this)
                 }
             }
@@ -177,8 +177,8 @@
             var receivedLevel = -1
 
             val listener = object : Consumer<Int> {
-                override fun accept(level: Int) {
-                    receivedLevel = level
+                override fun accept(value: Int) {
+                    receivedLevel = value
                     activity.removeOnTrimMemoryListener(this)
                 }
             }
@@ -262,8 +262,8 @@
             val receivedIntents = mutableListOf<Intent>()
 
             val listener = object : Consumer<Intent> {
-                override fun accept(intent: Intent) {
-                    receivedIntents += intent
+                override fun accept(value: Intent) {
+                    receivedIntents += value
                     activity.removeOnNewIntentListener(this)
                 }
             }
@@ -366,8 +366,8 @@
             lateinit var receivedInfo: MultiWindowModeChangedInfo
 
             val listener = object : Consumer<MultiWindowModeChangedInfo> {
-                override fun accept(info: MultiWindowModeChangedInfo) {
-                    receivedInfo = info
+                override fun accept(value: MultiWindowModeChangedInfo) {
+                    receivedInfo = value
                     activity.removeOnMultiWindowModeChangedListener(this)
                 }
             }
@@ -461,8 +461,8 @@
             lateinit var receivedInfo: PictureInPictureModeChangedInfo
 
             val listener = object : Consumer<PictureInPictureModeChangedInfo> {
-                override fun accept(info: PictureInPictureModeChangedInfo) {
-                    receivedInfo = info
+                override fun accept(value: PictureInPictureModeChangedInfo) {
+                    receivedInfo = value
                     activity.removeOnPictureInPictureModeChangedListener(this)
                 }
             }
diff --git a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityTest.kt b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityTest.kt
new file mode 100644
index 0000000..489eaa1
--- /dev/null
+++ b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.activity
+
+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 org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class ComponentActivityTest {
+
+    @Test
+    fun accessOnBackPressedDispatcher() {
+        withUse(ActivityScenario.launch(ComponentActivity::class.java)) {
+            val dispatcher = withActivity {
+                // Access the OnBackPressedDispatcher directly
+                onBackPressedDispatcher
+            }
+            assertThat(dispatcher).isNotNull()
+        }
+    }
+
+    @Test
+    fun accessOnBackPressedDispatcherBackgroundThread() {
+        withUse(ActivityScenario.launch(ComponentActivity::class.java)) {
+            val activity = withActivity { this }
+            // Access the OnBackPressedDispatcher on the test thread
+            val dispatcher = activity.onBackPressedDispatcher
+            assertThat(dispatcher).isNotNull()
+        }
+    }
+
+    @Test
+    fun earlyAccessOnBackPressedDispatcher() {
+        withUse(ActivityScenario.launch(EarlyDispatcherAccessComponentActivity::class.java)) {
+            val dispatcher = withActivity {
+                // Access the OnBackPressedDispatcher directly
+                onBackPressedDispatcher
+            }
+            assertThat(dispatcher).isNotNull()
+        }
+    }
+}
+
+class EarlyDispatcherAccessComponentActivity : ComponentActivity() {
+
+    init {
+        // Access the OnBackPressedDispatcher before the activity
+        // has reached the CREATED state
+        onBackPressedDispatcher
+    }
+}
diff --git a/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherInvokerTest.kt b/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherInvokerTest.kt
index b0fefbf..a02a5eb 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherInvokerTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherInvokerTest.kt
@@ -448,4 +448,58 @@
 
         assertThat(unregisterCount).isEqualTo(1)
     }
+
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @Test
+    fun testDoubleStartCallbackCausesCancel() {
+        var registerCount = 0
+        var unregisterCount = 0
+        val invoker = object : OnBackInvokedDispatcher {
+            override fun registerOnBackInvokedCallback(p0: Int, p1: OnBackInvokedCallback) {
+                registerCount++
+            }
+
+            override fun unregisterOnBackInvokedCallback(p0: OnBackInvokedCallback) {
+                unregisterCount++
+            }
+        }
+
+        val dispatcher = OnBackPressedDispatcher()
+
+        dispatcher.setOnBackInvokedDispatcher(invoker)
+
+        var cancelledCount = 0
+        val callback1 = object : OnBackPressedCallback(true) {
+            override fun handleOnBackStarted(backEvent: BackEventCompat) { }
+            override fun handleOnBackProgressed(backEvent: BackEventCompat) {}
+            override fun handleOnBackPressed() { }
+            override fun handleOnBackCancelled() {
+                cancelledCount++
+            }
+        }
+
+        dispatcher.addCallback(callback1)
+
+        assertThat(registerCount).isEqualTo(1)
+
+        dispatcher.dispatchOnBackStarted(BackEventCompat(0.1F, 0.1F, 0.1F, EDGE_LEFT))
+
+        val callback2 = object : OnBackPressedCallback(true) {
+            override fun handleOnBackStarted(backEvent: BackEventCompat) { }
+            override fun handleOnBackProgressed(backEvent: BackEventCompat) {}
+            override fun handleOnBackPressed() { }
+            override fun handleOnBackCancelled() { }
+        }
+
+        dispatcher.addCallback(callback2)
+
+        assertThat(registerCount).isEqualTo(2)
+
+        dispatcher.dispatchOnBackStarted(BackEventCompat(0.1F, 0.1F, 0.1F, EDGE_LEFT))
+
+        assertThat(cancelledCount).isEqualTo(1)
+
+        assertThat(unregisterCount).isEqualTo(1)
+    }
 }
diff --git a/activity/activity/src/main/java/androidx/activity/ComponentActivity.kt b/activity/activity/src/main/java/androidx/activity/ComponentActivity.kt
index b124e86..bb33a40 100644
--- a/activity/activity/src/main/java/androidx/activity/ComponentActivity.kt
+++ b/activity/activity/src/main/java/androidx/activity/ComponentActivity.kt
@@ -89,10 +89,12 @@
 import androidx.lifecycle.ViewModelStore
 import androidx.lifecycle.ViewModelStoreOwner
 import androidx.lifecycle.enableSavedStateHandles
+import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.setViewTreeLifecycleOwner
 import androidx.lifecycle.setViewTreeViewModelStoreOwner
 import androidx.lifecycle.viewmodel.CreationExtras
 import androidx.lifecycle.viewmodel.MutableCreationExtras
+import androidx.lifecycle.whenCreated
 import androidx.savedstate.SavedStateRegistry
 import androidx.savedstate.SavedStateRegistryController
 import androidx.savedstate.SavedStateRegistryOwner
@@ -101,6 +103,7 @@
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicInteger
+import kotlinx.coroutines.launch
 
 /**
  * Base class for activities that enables composition of higher level components.
@@ -637,17 +640,17 @@
                 }
             }
         }.also { dispatcher ->
-            lifecycle.addObserver(LifecycleEventObserver { lifecycleOwner, event ->
-                if (event == Lifecycle.Event.ON_CREATE) {
+            lifecycleScope.launch {
+                whenCreated {
                     if (Build.VERSION.SDK_INT >= 33) {
                         dispatcher.setOnBackInvokedDispatcher(
                             Api33Impl.getOnBackInvokedDispatcher(
-                                lifecycleOwner as ComponentActivity
+                                this@ComponentActivity
                             )
                         )
                     }
                 }
-            })
+            }
         }
     }
 
diff --git a/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt b/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt
index 04d0388..094d95e 100644
--- a/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt
+++ b/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt
@@ -233,6 +233,9 @@
         val callback = onBackPressedCallbacks.lastOrNull {
             it.isEnabled
         }
+        if (inProgressCallback != null) {
+            onBackCancelled()
+        }
         inProgressCallback = callback
         if (callback != null) {
             callback.handleOnBackStarted(backEvent)
diff --git a/activity/integration-tests/testapp/build.gradle b/activity/integration-tests/testapp/build.gradle
index c2e0a80..24d7a2e 100644
--- a/activity/integration-tests/testapp/build.gradle
+++ b/activity/integration-tests/testapp/build.gradle
@@ -35,7 +35,7 @@
     implementation("androidx.core:core-splashscreen:1.0.0")
 
     // Manually align dependencies across debugRuntime and debugAndroidTestRuntime.
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
 
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.testExtJunit)
diff --git a/annotation/annotation-experimental-lint/integration-tests/build.gradle b/annotation/annotation-experimental-lint/integration-tests/build.gradle
index b626e67..abe144b 100644
--- a/annotation/annotation-experimental-lint/integration-tests/build.gradle
+++ b/annotation/annotation-experimental-lint/integration-tests/build.gradle
@@ -40,7 +40,7 @@
 
 dependencies {
     implementation(libs.kotlinStdlib)
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
 }
 
 // Allow usage of Kotlin's @Experimental annotation, which is itself experimental.
diff --git a/annotation/annotation-replacewith-lint/build.gradle b/annotation/annotation-replacewith-lint/build.gradle
new file mode 100644
index 0000000..918162c
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/build.gradle
@@ -0,0 +1,48 @@
+/*
+ * 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("kotlin")
+}
+
+sourceSets {
+    test.resources.srcDirs(
+            project(":annotation:annotation-replacewith-lint-integration-tests")
+                    .projectDir.toString() + "/src/main"
+    )
+}
+
+dependencies {
+    compileOnly(libs.androidLintMinApi)
+    compileOnly(libs.kotlinStdlib)
+
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintTests)
+    testImplementation(libs.junit)
+}
+
+androidx {
+    name = "ReplaceWith annotation lint checks"
+    type = LibraryType.LINT
+    mavenVersion = LibraryVersions.ANNOTATION_REPLACEWITH
+    inceptionYear = "2024"
+    description = "Lint checks for the ReplaceWith annotation library. Also enforces the " +
+            "semantics of Kotlin @Deprecated API replacements from within Java source code."
+}
diff --git a/annotation/annotation-replacewith-lint/integration-tests/build.gradle b/annotation/annotation-replacewith-lint/integration-tests/build.gradle
new file mode 100644
index 0000000..4211c69
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/integration-tests/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * 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
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+def isIdeBuild() {
+    return project.properties['android.injected.invoked.from.ide'] == 'true'
+}
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+}
+
+android {
+    namespace "androidx.annotation.replacewith.lint.integrationtests"
+}
+
+dependencies {
+    implementation(libs.kotlinStdlib)
+    implementation(project(":annotation:annotation-replacewith"))
+}
+
+androidx {
+    type = LibraryType.INTERNAL_TEST_LIBRARY
+}
diff --git a/core/core/src/main/java/androidx/core/util/Supplier.java b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ConstructorNonStaticClass.java
similarity index 65%
copy from core/core/src/main/java/androidx/core/util/Supplier.java
copy to annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ConstructorNonStaticClass.java
index 9deba96..88368c4 100644
--- a/core/core/src/main/java/androidx/core/util/Supplier.java
+++ b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ConstructorNonStaticClass.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,14 @@
  * limitations under the License.
  */
 
-package androidx.core.util;
+package sample;
 
 /**
- * Compat version of {@link java.util.function.Supplier}
- * @param <T> the type of the input to the operation
+ * Usage of a static class constructor.
  */
-public interface Supplier<T> {
-
-    /**
-     * Gets a result.
-     *
-     * @return a result
-     */
-    T get();
+@SuppressWarnings({"unused", "deprecation", "InstantiationOfUtilityClass"})
+class ConstructorNonStaticClass {
+    void usage() {
+        new ReplaceWithUsageJava().new InnerClass("param");
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/util/Supplier.java b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ConstructorStaticClass.java
similarity index 65%
copy from core/core/src/main/java/androidx/core/util/Supplier.java
copy to annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ConstructorStaticClass.java
index 9deba96..d663e67 100644
--- a/core/core/src/main/java/androidx/core/util/Supplier.java
+++ b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ConstructorStaticClass.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,14 @@
  * limitations under the License.
  */
 
-package androidx.core.util;
+package sample;
 
 /**
- * Compat version of {@link java.util.function.Supplier}
- * @param <T> the type of the input to the operation
+ * Usage of a static class constructor.
  */
-public interface Supplier<T> {
-
-    /**
-     * Gets a result.
-     *
-     * @return a result
-     */
-    T get();
+@SuppressWarnings({"unused", "deprecation", "InstantiationOfUtilityClass"})
+class ConstructorStaticClass {
+    void usage() {
+        new ReplaceWithUsageJava("parameter");
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/util/Supplier.java b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ConstructorToStaticMethod.java
similarity index 65%
copy from core/core/src/main/java/androidx/core/util/Supplier.java
copy to annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ConstructorToStaticMethod.java
index 9deba96..fb62a31 100644
--- a/core/core/src/main/java/androidx/core/util/Supplier.java
+++ b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ConstructorToStaticMethod.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,14 @@
  * limitations under the License.
  */
 
-package androidx.core.util;
+package sample;
 
 /**
- * Compat version of {@link java.util.function.Supplier}
- * @param <T> the type of the input to the operation
+ * Usage of a static class constructor.
  */
-public interface Supplier<T> {
-
-    /**
-     * Gets a result.
-     *
-     * @return a result
-     */
-    T get();
+@SuppressWarnings({"unused", "deprecation", "InstantiationOfUtilityClass"})
+class ConstructorToStaticMethod {
+    void usage() {
+        new ReplaceWithUsageJava(10000);
+    }
 }
diff --git a/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/MethodExplicitThis.java b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/MethodExplicitThis.java
new file mode 100644
index 0000000..0bab04c
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/MethodExplicitThis.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample;
+
+import androidx.annotation.ReplaceWith;
+
+/**
+ * Usage with explicit "this" receiver.
+ */
+@SuppressWarnings({"deprecation", "unused"})
+class MethodExplicitThis {
+    @Deprecated
+    @ReplaceWith(expression = "newMethod(obj)")
+    void oldMethod(Object obj) {}
+
+    void newMethod(Object obj) {}
+
+    void usage() {
+        this.oldMethod(null);
+    }
+}
diff --git a/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/MethodImplicitThis.java b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/MethodImplicitThis.java
new file mode 100644
index 0000000..6cbc0a6
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/MethodImplicitThis.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample;
+
+import androidx.annotation.ReplaceWith;
+
+/**
+ * Usage with implicit "this" receiver.
+ */
+@SuppressWarnings({"deprecation", "unused"})
+class MethodImplicitThis {
+    @Deprecated
+    @ReplaceWith(expression = "newMethod(obj)")
+    void oldMethod(Object obj) {}
+
+    void newMethod(Object obj) {}
+
+    void usage() {
+        oldMethod(null);
+    }
+}
diff --git a/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ReplaceWithUsageJava.java b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ReplaceWithUsageJava.java
new file mode 100644
index 0000000..80da9c9
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ReplaceWithUsageJava.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample;
+
+import android.view.View;
+
+import androidx.annotation.ReplaceWith;
+
+@SuppressWarnings({"unused", "UnknownNullness", "InnerClassMayBeStatic",
+        "InstantiationOfUtilityClass", "ClassCanBeStatic", "PrivateConstructorForUtilityClass"})
+public class ReplaceWithUsageJava {
+    /**
+     * Calls the method on the object.
+     *
+     * @param obj The object on which to call the method.
+     * @deprecated Use {@link Object#toString()} directly.
+     */
+    @Deprecated
+    @ReplaceWith(expression = "obj.toString()")
+    public static void toString(Object obj) {
+        // Stub.
+    }
+
+    /**
+     * Returns a new object.
+     */
+    public static ReplaceWithUsageJava obtain(int param) {
+        return new ReplaceWithUsageJava();
+    }
+
+    /**
+     * String constant.
+     *
+     * @deprecated Use {@link View#AUTOFILL_HINT_NAME} directly.
+     */
+    @Deprecated
+    @ReplaceWith(expression = "View.AUTOFILL_HINT_NAME")
+    public static final String AUTOFILL_HINT_NAME = View.AUTOFILL_HINT_NAME;
+
+    /**
+     * Constructor.
+     *
+     * @deprecated Use {@link StringBuffer#StringBuffer(String)} instead.
+     */
+    @Deprecated
+    @ReplaceWith(expression = "StringBuffer(param)")
+    public ReplaceWithUsageJava(String param) {
+        // Stub.
+    }
+
+    /**
+     * Constructor.
+     *
+     * @deprecated Use {@link ReplaceWithUsageJava#obtain(int)} instead.
+     */
+    @Deprecated
+    @ReplaceWith(expression = "ReplaceWithUsageJava.newInstance(param)")
+    public ReplaceWithUsageJava(int param) {
+        // Stub.
+    }
+
+    /**
+     * Constructor.
+     */
+    public ReplaceWithUsageJava() {
+        // Stub.
+    }
+
+    class InnerClass {
+        /**
+         * Constructor.
+         *
+         * @deprecated Use {@link InnerClass#InnerClass()} instead.
+         */
+        @Deprecated
+        @ReplaceWith(expression = "InnerClass()")
+        InnerClass(String param) {
+            // Stub.
+        }
+
+        /**
+         * Constructor.
+         */
+        InnerClass() {
+            // Stub.
+        }
+    }
+}
diff --git a/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ReplaceWithUsageKotlin.kt b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ReplaceWithUsageKotlin.kt
new file mode 100644
index 0000000..375a0e2
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/ReplaceWithUsageKotlin.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("unused")
+
+package sample
+
+import android.view.View
+
+class ReplaceWithUsageKotlin {
+    companion object {
+        /**
+         * Calls the method on the object.
+         *
+         * @param obj The object on which to call the method.
+         */
+        @Deprecated("Use [Object#toString()] directly.", ReplaceWith("obj.toString()"))
+        @JvmStatic
+        fun toString(obj: Any) {
+            obj.toString()
+        }
+
+        /**
+         * String constant.
+         */
+        @Deprecated(
+            message = "Use {@link View#AUTOFILL_HINT_NAME} directly.",
+            ReplaceWith(expression = "View.AUTOFILL_HINT_NAME")
+        )
+        @JvmStatic
+        val AUTOFILL_HINT_NAME = View.AUTOFILL_HINT_NAME
+    }
+}
diff --git a/core/core/src/main/java/androidx/core/util/Supplier.java b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/StaticFieldExplicitClass.java
similarity index 65%
copy from core/core/src/main/java/androidx/core/util/Supplier.java
copy to annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/StaticFieldExplicitClass.java
index 9deba96..34cd81f 100644
--- a/core/core/src/main/java/androidx/core/util/Supplier.java
+++ b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/StaticFieldExplicitClass.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,14 @@
  * limitations under the License.
  */
 
-package androidx.core.util;
+package sample;
 
 /**
- * Compat version of {@link java.util.function.Supplier}
- * @param <T> the type of the input to the operation
+ * Usage of a static method with an explicit class.
  */
-public interface Supplier<T> {
-
-    /**
-     * Gets a result.
-     *
-     * @return a result
-     */
-    T get();
+@SuppressWarnings({"deprecation", "unused"})
+class StaticFieldExplicitClass {
+    void main() {
+        System.out.println(ReplaceWithUsageJava.AUTOFILL_HINT_NAME);
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/util/Supplier.java b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/StaticFieldImplicitClass.java
similarity index 63%
copy from core/core/src/main/java/androidx/core/util/Supplier.java
copy to annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/StaticFieldImplicitClass.java
index 9deba96..5611277 100644
--- a/core/core/src/main/java/androidx/core/util/Supplier.java
+++ b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/StaticFieldImplicitClass.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,16 @@
  * limitations under the License.
  */
 
-package androidx.core.util;
+package sample;
+
+import static sample.ReplaceWithUsageJava.AUTOFILL_HINT_NAME;
 
 /**
- * Compat version of {@link java.util.function.Supplier}
- * @param <T> the type of the input to the operation
+ * Usage of a static method with an explicit class.
  */
-public interface Supplier<T> {
-
-    /**
-     * Gets a result.
-     *
-     * @return a result
-     */
-    T get();
+@SuppressWarnings({"deprecation", "unused"})
+class StaticFieldImplicitClass {
+    void main() {
+        System.out.println(AUTOFILL_HINT_NAME);
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/util/Supplier.java b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/StaticMethodExplicitClass.java
similarity index 65%
copy from core/core/src/main/java/androidx/core/util/Supplier.java
copy to annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/StaticMethodExplicitClass.java
index 9deba96..c5e54fe 100644
--- a/core/core/src/main/java/androidx/core/util/Supplier.java
+++ b/annotation/annotation-replacewith-lint/integration-tests/src/main/java/sample/StaticMethodExplicitClass.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,14 @@
  * limitations under the License.
  */
 
-package androidx.core.util;
+package sample;
 
 /**
- * Compat version of {@link java.util.function.Supplier}
- * @param <T> the type of the input to the operation
+ * Usage of a static method with an explicit class.
  */
-public interface Supplier<T> {
-
-    /**
-     * Gets a result.
-     *
-     * @return a result
-     */
-    T get();
+@SuppressWarnings({"deprecation", "unused"})
+class StaticMethodExplicitClass {
+    void main() {
+        ReplaceWithUsageJava.toString(this);
+    }
 }
diff --git a/annotation/annotation-replacewith-lint/src/main/java/androidx/annotation/replacewith/lint/ReplaceWithDetector.kt b/annotation/annotation-replacewith-lint/src/main/java/androidx/annotation/replacewith/lint/ReplaceWithDetector.kt
new file mode 100644
index 0000000..ba130c81
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/src/main/java/androidx/annotation/replacewith/lint/ReplaceWithDetector.kt
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.annotation.replacewith.lint
+
+import com.android.tools.lint.detector.api.AnnotationUsageType
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.ConstantEvaluator
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.LintFix
+import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiField
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.impl.source.tree.TreeElement
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UQualifiedReferenceExpression
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.java.JavaConstructorUCallExpression
+
+class ReplaceWithDetector : Detector(), SourceCodeScanner {
+
+    override fun applicableAnnotations(): List<String> = listOf(
+        JAVA_REPLACE_WITH_ANNOTATION,
+        KOTLIN_DEPRECATED_ANNOTATION,
+    )
+
+    override fun visitAnnotationUsage(
+        context: JavaContext,
+        usage: UElement,
+        type: AnnotationUsageType,
+        annotation: UAnnotation,
+        qualifiedName: String,
+        method: PsiMethod?,
+        referenced: PsiElement?,
+        annotations: List<UAnnotation>,
+        allMemberAnnotations: List<UAnnotation>,
+        allClassAnnotations: List<UAnnotation>,
+        allPackageAnnotations: List<UAnnotation>
+    ) {
+        // Ignore callbacks for assignment on the original declaration of an annotated field.
+        if (type == AnnotationUsageType.ASSIGNMENT_RHS && usage.uastParent == referenced) return
+
+        when (qualifiedName) {
+            JAVA_REPLACE_WITH_ANNOTATION -> {
+                var location = context.getLocation(usage)
+                var expression = annotation.findAttributeValue("expression") ?.let { expr ->
+                    ConstantEvaluator.evaluate(context, expr)
+                } as? String ?: return
+
+                val includeReceiver = Regex("^\\w+\\.\\w+.*\$").matches(expression)
+                val includeArguments = Regex("^.*\\w+\\(.*\\)$").matches(expression)
+
+                if (method != null && usage is UCallExpression) {
+                    // Per Kotlin documentation for ReplaceWith: For function calls, the replacement
+                    // expression may contain argument names of the deprecated function, which will
+                    // be substituted with actual parameters used in the call being updated.
+                    val argumentNamesToActualParams = method.parameters.mapIndexed { index, param ->
+                        param.name to usage.getArgumentForParameter(index)?.sourcePsi?.text
+                    }.associate { it }
+
+                    // Tokenize the replacement expression using a regex, replacing as we go. This
+                    // isn't the most efficient approach (e.g. trie) but it's easy to write.
+                    val search = Regex("\\w+")
+                    var index = 0
+                    do {
+                        val matchResult = search.find(expression, index) ?: break
+                        val replacement = argumentNamesToActualParams[matchResult.value]
+                        if (replacement != null) {
+                            expression = expression.replaceRange(matchResult.range, replacement)
+                            index += replacement.length
+                        } else {
+                            index += matchResult.value.length
+                        }
+                    } while (index < expression.length)
+
+                    location = when (usage) {
+                        is JavaConstructorUCallExpression -> {
+                            // The expression should never specify "new", but if it specifies a
+                            // receiver then we should replace the call to "new". For example, if
+                            // we're replacing `new Clazz("arg")` with `ClazzCompat.create("arg")`.
+                            context.getConstructorLocation(
+                                usage, includeReceiver, includeArguments
+                            )
+                        }
+                        else -> {
+                            // The expression may optionally specify a receiver or arguments, in
+                            // which case we should include the originals in the replacement range.
+                            context.getCallLocation(usage, includeReceiver, includeArguments)
+                        }
+                    }
+                } else if (referenced is PsiField && usage is USimpleNameReferenceExpression) {
+                    // The expression may optionally specify a receiver, in which case we should
+                    // include the original in the replacement range.
+                    if (includeReceiver) {
+                        // If this is a qualified reference and we're including the "receiver" then
+                        // we should replace the fully-qualified expression.
+                        (usage.uastParent as? UQualifiedReferenceExpression)?.let { reference ->
+                            location = context.getLocation(reference)
+                        }
+                    }
+                }
+
+                reportLintFix(context, usage, location, expression)
+            }
+        }
+    }
+
+    private fun reportLintFix(
+        context: JavaContext,
+        usage: UElement,
+        location: Location,
+        expression: String,
+    ) {
+        context.report(ISSUE, usage, location, "Replacement available",
+            createLintFix(location, expression))
+    }
+
+    private fun createLintFix(location: Location, expression: String): LintFix =
+        fix().replace().range(location).name("Replace with `$expression`").with(expression).build()
+
+    companion object {
+        private val IMPLEMENTATION = Implementation(
+            ReplaceWithDetector::class.java,
+            Scope.JAVA_FILE_SCOPE,
+        )
+
+        const val KOTLIN_DEPRECATED_ANNOTATION = "kotlin.Deprecated"
+        const val JAVA_REPLACE_WITH_ANNOTATION = "androidx.annotation.ReplaceWith"
+
+        val ISSUE = Issue.create(
+            id = "ReplaceWith",
+            briefDescription = "Replacement available",
+            explanation = "A recommended replacement is available for this API usage.",
+            category = Category.CORRECTNESS,
+            priority = 4,
+            severity = Severity.INFORMATIONAL,
+            implementation = IMPLEMENTATION,
+        )
+    }
+}
+
+/**
+ * Modified version of [JavaContext.getRangeLocation] that uses the `classReference` instead of the
+ * `receiver` to handle trimming the `new` keyword from the start of a Java constructor call.
+ */
+fun JavaContext.getConstructorLocation(
+    call: JavaConstructorUCallExpression,
+    includeNew: Boolean,
+    includeArguments: Boolean
+): Location {
+    if (includeArguments) {
+        call.valueArguments.lastOrNull()?.let { lastArgument ->
+            val argumentsEnd = lastArgument.sourcePsi?.endOffset
+            val callEnds = call.sourcePsi.endOffset
+            if (argumentsEnd != null && argumentsEnd > callEnds) {
+                // The call element has arguments that are outside of its own range.
+                // This typically means users are making a function call using
+                // assignment syntax, e.g. key = value instead of setKey(value);
+                // here the call range is just "key" and the arguments range is "value".
+                // Create a range which merges these two.
+                val startElement = if (!includeNew) call.classReference ?: call else call
+                // Work around UAST bug where the value argument list points directly to the
+                // string content node instead of a node containing the opening and closing
+                // tokens as well. We need to include the closing tags in the range as well!
+                val next = (lastArgument.sourcePsi as? KtLiteralStringTemplateEntry)
+                    ?.nextSibling as? TreeElement
+                val delta =
+                    if (next != null && next.elementType == KtTokens.CLOSING_QUOTE) {
+                        next.textLength
+                    } else {
+                        0
+                    }
+                return getRangeLocation(startElement, 0, lastArgument, delta)
+            }
+        }
+    }
+
+    val classReference = call.classReference
+    if (includeNew || classReference == null) {
+        if (includeArguments) {
+            // Method with arguments but no receiver is the default range for UCallExpressions
+            // modulo the scenario with arguments outside the call, handled at the beginning
+            // of this method
+            return getLocation(call)
+        }
+        // Just the method name
+        val methodIdentifier = call.methodIdentifier
+        if (methodIdentifier != null) {
+            return getLocation(methodIdentifier)
+        }
+    } else {
+        if (!includeArguments) {
+            val methodIdentifier = call.methodIdentifier
+            if (methodIdentifier != null) {
+                return getRangeLocation(classReference, 0, methodIdentifier, 0)
+            }
+        }
+
+        // Use PsiElement variant of getRangeLocation because UElement variant returns wrong results
+        // when the `from` argument starts after the `to` argument, as it does for the constructor
+        // class reference.
+        return getRangeLocation(classReference.javaPsi!!, 0, call.javaPsi!!, 0)
+    }
+
+    return getLocation(call)
+}
diff --git a/annotation/annotation-replacewith-lint/src/main/java/androidx/annotation/replacewith/lint/ReplaceWithIssueRegistry.kt b/annotation/annotation-replacewith-lint/src/main/java/androidx/annotation/replacewith/lint/ReplaceWithIssueRegistry.kt
new file mode 100644
index 0000000..da3620f
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/src/main/java/androidx/annotation/replacewith/lint/ReplaceWithIssueRegistry.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.annotation.replacewith.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.client.api.Vendor
+import com.android.tools.lint.detector.api.CURRENT_API
+
+@Suppress("UnstableApiUsage")
+class ReplaceWithIssueRegistry : IssueRegistry() {
+    override val minApi = CURRENT_API
+    override val api = 14
+    override val issues get() = listOf(ReplaceWithDetector.ISSUE)
+    override val vendor = Vendor(
+        feedbackUrl = "https://issuetracker.google.com/issues/new?component=459778",
+        identifier = "androidx.annotation.replacewith",
+        vendorName = "Android Open Source Project",
+    )
+}
diff --git a/annotation/annotation-replacewith-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry b/annotation/annotation-replacewith-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
new file mode 100644
index 0000000..9ebd6a0
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
@@ -0,0 +1 @@
+androidx.annotation.replacewith.lint.ReplaceWithIssueRegistry
diff --git a/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ApiLintVersionsTest.kt b/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ApiLintVersionsTest.kt
new file mode 100644
index 0000000..938b868
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ApiLintVersionsTest.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.annotation.replacewith.lint
+
+import com.android.tools.lint.client.api.LintClient
+import com.android.tools.lint.detector.api.CURRENT_API
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ApiLintVersionsTest {
+
+    @Test
+    fun versionsCheck() {
+        LintClient.clientName = LintClient.CLIENT_UNIT_TESTS
+
+        val registry = ReplaceWithIssueRegistry()
+        // We hardcode version registry.api to the version that is used to run tests.
+        assertEquals("registry.api matches version used to run tests", CURRENT_API, registry.api)
+        // Intentionally fails in IDE, because we use different API version in Studio and CLI.
+        assertEquals("registry.minApi is set to minimum level of 10", 10, registry.minApi)
+    }
+}
diff --git a/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ReplaceWithDetectorConstructorTest.kt b/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ReplaceWithDetectorConstructorTest.kt
new file mode 100644
index 0000000..013239e
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ReplaceWithDetectorConstructorTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.annotation.replacewith.lint;
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ReplaceWithDetectorConstructorTest {
+
+    @Test
+    fun constructorStaticClass() {
+        val input = arrayOf(
+                javaSample("sample.ReplaceWithUsageJava"),
+                javaSample("sample.ConstructorStaticClass")
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/sample/ConstructorStaticClass.java:25: Information: Replacement available [ReplaceWith]
+        new ReplaceWithUsageJava("parameter");
+            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+0 errors, 0 warnings
+        """.trimIndent()
+
+        val expectedFixDiffs = """
+Fix for src/sample/ConstructorStaticClass.java line 25: Replace with `StringBuffer("parameter")`:
+@@ -25 +25
+-         new ReplaceWithUsageJava("parameter");
++         new StringBuffer("parameter");
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
+    }
+
+    @Test
+    fun constructorNonStaticClass() {
+        val input = arrayOf(
+            javaSample("sample.ReplaceWithUsageJava"),
+            javaSample("sample.ConstructorNonStaticClass")
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/sample/ConstructorNonStaticClass.java:25: Information: Replacement available [ReplaceWith]
+        new ReplaceWithUsageJava().new InnerClass("param");
+                                       ~~~~~~~~~~~~~~~~~~~
+0 errors, 0 warnings
+        """.trimIndent()
+
+        val expectedFixDiffs = """
+Fix for src/sample/ConstructorNonStaticClass.java line 25: Replace with `InnerClass()`:
+@@ -25 +25
+-         new ReplaceWithUsageJava().new InnerClass("param");
++         new ReplaceWithUsageJava().new InnerClass();
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
+    }
+
+    @Test
+    fun constructorToStaticMethod() {
+        val input = arrayOf(
+            javaSample("sample.ReplaceWithUsageJava"),
+            javaSample("sample.ConstructorToStaticMethod")
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/sample/ConstructorToStaticMethod.java:25: Information: Replacement available [ReplaceWith]
+        new ReplaceWithUsageJava(10000);
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+0 errors, 0 warnings
+        """.trimIndent()
+
+        val expectedFixDiffs = """
+Fix for src/sample/ConstructorToStaticMethod.java line 25: Replace with `ReplaceWithUsageJava.newInstance(10000)`:
+@@ -25 +25
+-         new ReplaceWithUsageJava(10000);
++         ReplaceWithUsageJava.newInstance(10000);
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
+    }
+}
diff --git a/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ReplaceWithDetectorFieldTest.kt b/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ReplaceWithDetectorFieldTest.kt
new file mode 100644
index 0000000..22e983a
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ReplaceWithDetectorFieldTest.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.annotation.replacewith.lint
+
+import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ReplaceWithDetectorFieldTest {
+
+    @Test
+    fun staticFieldExplicitClass() {
+        val input = arrayOf(
+            javaSample("sample.ReplaceWithUsageJava"),
+            javaSample("sample.StaticFieldExplicitClass")
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/sample/StaticFieldExplicitClass.java:25: Information: Replacement available [ReplaceWith]
+        System.out.println(ReplaceWithUsageJava.AUTOFILL_HINT_NAME);
+                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+0 errors, 0 warnings
+        """.trimIndent()
+
+        val expectedFixDiffs = """
+Fix for src/sample/StaticFieldExplicitClass.java line 25: Replace with `View.AUTOFILL_HINT_NAME`:
+@@ -25 +25
+-         System.out.println(ReplaceWithUsageJava.AUTOFILL_HINT_NAME);
++         System.out.println(View.AUTOFILL_HINT_NAME);
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
+    }
+
+    @Test
+    fun staticFieldImplicitClass() {
+        val input = arrayOf(
+            javaSample("sample.ReplaceWithUsageJava"),
+            javaSample("sample.StaticFieldImplicitClass")
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/sample/StaticFieldImplicitClass.java:27: Information: Replacement available [ReplaceWith]
+        System.out.println(AUTOFILL_HINT_NAME);
+                           ~~~~~~~~~~~~~~~~~~
+0 errors, 0 warnings
+        """.trimIndent()
+
+        val expectedFixDiffs = """
+Fix for src/sample/StaticFieldImplicitClass.java line 27: Replace with `View.AUTOFILL_HINT_NAME`:
+@@ -27 +27
+-         System.out.println(AUTOFILL_HINT_NAME);
++         System.out.println(View.AUTOFILL_HINT_NAME);
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
+    }
+}
diff --git a/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ReplaceWithDetectorMethodTest.kt b/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ReplaceWithDetectorMethodTest.kt
new file mode 100644
index 0000000..e1b4e28
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/ReplaceWithDetectorMethodTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.annotation.replacewith.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ReplaceWithDetectorMethodTest {
+
+    @Test
+    fun staticMethodExplicitClass() {
+        val input = arrayOf(
+            javaSample("sample.ReplaceWithUsageJava"),
+            javaSample("sample.StaticMethodExplicitClass")
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/sample/StaticMethodExplicitClass.java:25: Information: Replacement available [ReplaceWith]
+        ReplaceWithUsageJava.toString(this);
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+0 errors, 0 warnings
+        """.trimIndent()
+
+        val expectedFixDiffs = """
+Fix for src/sample/StaticMethodExplicitClass.java line 25: Replace with `this.toString()`:
+@@ -25 +25
+-         ReplaceWithUsageJava.toString(this);
++         this.toString();
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
+    }
+
+    @Test
+    fun methodImplicitThis() {
+        val input = arrayOf(
+            javaSample("sample.MethodImplicitThis")
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/sample/MethodImplicitThis.java:33: Information: Replacement available [ReplaceWith]
+        oldMethod(null);
+        ~~~~~~~~~~~~~~~
+0 errors, 0 warnings
+        """.trimIndent()
+
+        val expectedFixDiffs = """
+Fix for src/sample/MethodImplicitThis.java line 33: Replace with `newMethod(null)`:
+@@ -33 +33
+-         oldMethod(null);
++         newMethod(null);
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
+    }
+
+    @Test
+    fun methodExplicitThis() {
+        val input = arrayOf(
+            javaSample("sample.MethodExplicitThis")
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+src/sample/MethodExplicitThis.java:33: Information: Replacement available [ReplaceWith]
+        this.oldMethod(null);
+             ~~~~~~~~~~~~~~~
+0 errors, 0 warnings
+        """.trimIndent()
+
+        val expectedFixDiffs = """
+Fix for src/sample/MethodExplicitThis.java line 33: Replace with `newMethod(null)`:
+@@ -33 +33
+-         this.oldMethod(null);
++         this.newMethod(null);
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
+    }
+}
diff --git a/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/TestUtils.kt b/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/TestUtils.kt
new file mode 100644
index 0000000..bcb1dc6
--- /dev/null
+++ b/annotation/annotation-replacewith-lint/src/test/kotlin/androidx/annotation/replacewith/lint/TestUtils.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.annotation.replacewith.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.checks.infrastructure.TestLintResult
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+
+fun check(vararg testFiles: TestFile): TestLintResult {
+    return TestLintTask.lint()
+        .files(
+            ANDROIDX_REPLACE_WITH_KT,
+            *testFiles
+        )
+        .issues(ReplaceWithDetector.ISSUE)
+        .run()
+}
+
+/**
+ * Loads a [TestFile] from Java source code included in the JAR resources.
+ */
+fun javaSample(className: String): TestFile {
+    return TestFiles.java(
+        ReplaceWithDetectorMethodTest::class.java.getResource(
+            "/java/${className.replace('.', '/')}.java"
+        )!!.readText()
+    )
+}
+
+/**
+ * Loads a [TestFile] from Kotlin source code included in the JAR resources.
+ */
+fun ktSample(className: String): TestFile {
+    return TestFiles.kotlin(
+        ReplaceWithDetectorMethodTest::class.java.getResource(
+            "/java/${className.replace('.', '/')}.kt"
+        )!!.readText()
+    )
+}
+
+/**
+ * [TestFile] containing ReplaceWith.kt from the ReplaceWith annotation library.
+ *
+ * This is a workaround for IntelliJ failing to recognize source files if they are also
+ * included as resources.
+ */
+val ANDROIDX_REPLACE_WITH_KT: TestFile = TestFiles.kotlin(
+    """
+            package androidx.annotation
+
+            @Retention(AnnotationRetention.BINARY)
+            @Target(
+                AnnotationTarget.CLASS,
+                AnnotationTarget.FUNCTION,
+                AnnotationTarget.PROPERTY,
+                AnnotationTarget.ANNOTATION_CLASS,
+                AnnotationTarget.CONSTRUCTOR,
+                AnnotationTarget.PROPERTY_SETTER,
+                AnnotationTarget.PROPERTY_GETTER,
+                AnnotationTarget.TYPEALIAS
+            )
+            @java.lang.annotation.Target(
+                ElementType.CONSTRUCTOR,
+                ElementType.FIELD,
+                ElementType.METHOD,
+                ElementType.TYPE,
+            )
+            annotation class ReplaceWith(
+                val expression: String,
+                vararg val imports: String
+            )
+            """.trimIndent()
+)
diff --git a/annotation/annotation-replacewith/api/current.txt b/annotation/annotation-replacewith/api/current.txt
new file mode 100644
index 0000000..1147234
--- /dev/null
+++ b/annotation/annotation-replacewith/api/current.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.annotation {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.CONSTRUCTOR}) public @interface ReplaceWith {
+    method public abstract String expression();
+  }
+
+}
+
diff --git a/annotation/annotation-replacewith/api/res-current.txt b/annotation/annotation-replacewith/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/annotation/annotation-replacewith/api/res-current.txt
diff --git a/annotation/annotation-replacewith/api/restricted_current.txt b/annotation/annotation-replacewith/api/restricted_current.txt
new file mode 100644
index 0000000..1147234
--- /dev/null
+++ b/annotation/annotation-replacewith/api/restricted_current.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.annotation {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.CONSTRUCTOR}) public @interface ReplaceWith {
+    method public abstract String expression();
+  }
+
+}
+
diff --git a/annotation/annotation-replacewith/build.gradle b/annotation/annotation-replacewith/build.gradle
new file mode 100644
index 0000000..97cd77b
--- /dev/null
+++ b/annotation/annotation-replacewith/build.gradle
@@ -0,0 +1,46 @@
+/*
+ * 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.KotlinTarget
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(libs.kotlinStdlib)
+    lintPublish(project(":annotation:annotation-replacewith-lint"))
+}
+
+androidx {
+    name = "ReplaceWith annotation"
+    publish = Publish.SNAPSHOT_AND_RELEASE
+    mavenVersion = LibraryVersions.ANNOTATION_REPLACEWITH
+    kotlinTarget = KotlinTarget.KOTLIN_1_7
+    inceptionYear = "2019"
+    description = "Java annotation for use on deprecated API surfaces with a replacement. When " +
+            "used in conjunction with the ReplaceWith annotation lint checks, this annotation " +
+            "provides functional parity with Kotlin's Deprecated annotation ReplaceWith feature."
+    metalavaK2UastEnabled = true
+}
+
+android {
+    namespace "androidx.annotation.replacewith"
+}
diff --git a/annotation/annotation-replacewith/src/main/java/androidx/annotation/ReplaceWith.java b/annotation/annotation-replacewith/src/main/java/androidx/annotation/ReplaceWith.java
new file mode 100644
index 0000000..a9c70b9
--- /dev/null
+++ b/annotation/annotation-replacewith/src/main/java/androidx/annotation/ReplaceWith.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Target;
+
+/**
+ * Specifies a code fragment that can be used to suggest a replacement for a method in
+ * conjunction with the `ReplaceWith` lint check.
+ * <p>
+ * The {@code expression} parameter specified the replacement expression, which is interpreted in
+ * the context of the symbol being used and can reference members of the enclosing classes, etc.
+ * <p>
+ * For method calls, the replacement expression may contain parameter names of the method being
+ * replaced, which will be substituted with actual arguments used in the call being replaced:
+ * <pre>
+ * &#64;ReplaceWith(expression = "event.getActionType(slot)")
+ * static int getActionType(AccessibilityEvent event, int slot) { ... }
+ * </pre>
+ */
+@Target({METHOD, FIELD, CONSTRUCTOR})
+public @interface ReplaceWith {
+    String expression();
+}
diff --git a/appactions/interaction/interaction-service/build.gradle b/appactions/interaction/interaction-service/build.gradle
index 6659821..24af205 100644
--- a/appactions/interaction/interaction-service/build.gradle
+++ b/appactions/interaction/interaction-service/build.gradle
@@ -51,7 +51,7 @@
     implementation(libs.jsr250)
 
     // Force upgrade since 1.2.0 is not compatible with latest lint.
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
 
     testImplementation(project(":appactions:interaction:interaction-capabilities-core"))
     testImplementation(project(":appactions:interaction:interaction-capabilities-testing"))
diff --git a/appcompat/integration-tests/receive-content-testapp/build.gradle b/appcompat/integration-tests/receive-content-testapp/build.gradle
index cdacff2..0a9360b 100644
--- a/appcompat/integration-tests/receive-content-testapp/build.gradle
+++ b/appcompat/integration-tests/receive-content-testapp/build.gradle
@@ -35,7 +35,7 @@
     implementation(libs.material)
 
     // Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
 
     androidTestImplementation("androidx.lifecycle:lifecycle-common:2.6.1")
     androidTestImplementation(libs.testCore)
diff --git a/benchmark/benchmark-common/build.gradle b/benchmark/benchmark-common/build.gradle
index 10990d6..c1f2b53 100644
--- a/benchmark/benchmark-common/build.gradle
+++ b/benchmark/benchmark-common/build.gradle
@@ -74,7 +74,7 @@
 dependencies {
     implementation(libs.kotlinStdlib)
     api("androidx.annotation:annotation:1.7.0")
-    api(project(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
     implementation("androidx.tracing:tracing-ktx:1.0.0")
     implementation(project(":tracing:tracing-perfetto-handshake"))
     implementation("androidx.test:monitor:1.6.1")
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/DeviceInfoTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/DeviceInfoTest.kt
index 2ac03e5..ddcc1d0 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/DeviceInfoTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/DeviceInfoTest.kt
@@ -24,6 +24,7 @@
 import kotlin.test.assertEquals
 import kotlin.test.assertFalse
 import kotlin.test.assertTrue
+import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -49,7 +50,26 @@
     }
 
     @Test
+    fun artMainlineVersionWembley() { // specific regression test for b/319541718
+        assumeTrue(Build.DEVICE.startsWith("wembley"))
+
+        // Double checks some special properties of go devices
+        assertTrue(DeviceInfo.isLowRamDevice)
+
+        // Wembley available versions don't hit any of the method tracing issues, no art mainline
+        assertFalse(DeviceInfo.methodTracingAffectsMeasurements)
+        assertEquals(-1, DeviceInfo.artMainlineVersion)
+    }
+
+    @Test
     fun artMainlineVersion() {
+        // bypass main test if appear to be on go device without art mainline module
+        if (Build.VERSION.SDK_INT in 31..33 && DeviceInfo.isLowRamDevice) {
+            if (DeviceInfo.artMainlineVersion == -1L) {
+                return // bypass rest of test, appear to be on go device
+            }
+        }
+
         if (Build.VERSION.SDK_INT >= 30) {
             // validate we have a reasonable looking number
             if (Build.VERSION.SDK_INT >= 31) {
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
index 8be8808..874dc2a 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
@@ -16,18 +16,20 @@
 
 package androidx.benchmark
 
+import android.app.ActivityManager
+import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import android.os.BatteryManager
 import android.os.Build
+import android.util.Log
 import android.util.Printer
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.test.platform.app.InstrumentationRegistry
 import java.io.File
-import java.lang.IllegalStateException
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 object DeviceInfo {
@@ -96,28 +98,6 @@
     val misconfiguredForTracing = !File("/sys/kernel/tracing/trace_marker").exists() &&
         !File("/sys/kernel/debug/tracing/trace_marker").exists()
 
-    val artMainlineVersion = when {
-        Build.VERSION.SDK_INT >= 31 ->
-            queryArtMainlineVersion()
-        Build.VERSION.SDK_INT == 30 ->
-            1
-        else ->
-            -1
-    }
-
-    /**
-     * Starting with the first Android U release, ART mainline drops optimizations after method
-     * tracing occurs, so we disable tracing on those mainline versions.
-     *
-     * TODO: update max value once a fix is released to mainline
-     * See b/303660864
-     */
-    private val ART_MAINLINE_MIN_VERSIONS_AFFECTING_METHOD_TRACING = 340000000L..Long.MAX_VALUE
-
-    val methodTracingAffectsMeasurements =
-        Build.VERSION.SDK_INT in 26..30 || // b/313868903
-            artMainlineVersion in ART_MAINLINE_MIN_VERSIONS_AFFECTING_METHOD_TRACING // b/303660864
-
     private fun getMainlineAppInfo(packageName: String): ApplicationInfo? {
         return try {
             InstrumentationRegistry.getInstrumentation().context.packageManager
@@ -131,7 +111,19 @@
     private fun queryArtMainlineVersion(): Long {
         val artMainlinePackage = getMainlineAppInfo("com.google.android.art")
             ?: getMainlineAppInfo("com.android.art")
-            ?: throw IllegalStateException("Unable to find installed ART mainline module")
+
+        if (artMainlinePackage == null) {
+            check(Build.VERSION.SDK_INT in 31..33 && isLowRamDevice) {
+                "Unable to find installed ART mainline module," +
+                    " sdk ${Build.VERSION.SDK_INT}, isLowRamDevice $isLowRamDevice"
+            }
+            // accept missing module if it appears we're on a go device
+            Log.d(
+                BenchmarkState.TAG,
+                "No ART mainline module found, appears to be go device prior to mainline support"
+            )
+            return -1
+        }
 
         // This is an EXTREMELY SILLY way to find out ART's versions, but I couldn't find a better
         // one without reflecting into ApplicationInfo.longVersionCode (not allowed in jetpack)
@@ -161,6 +153,8 @@
         return versionCode
     }
 
+    val isLowRamDevice: Boolean
+
     init {
         val context = InstrumentationRegistry.getInstrumentation().targetContext
 
@@ -176,6 +170,9 @@
             level * 100 / scale
         } ?: 100
 
+        isLowRamDevice =
+            (context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).isLowRamDevice
+
         deviceSummaryString = "DeviceInfo(Brand=${Build.BRAND}" +
             ", Model=${Build.MODEL}" +
             ", SDK=${Build.VERSION.SDK_INT}" +
@@ -217,4 +214,27 @@
             )
         )
     }
+
+    val artMainlineVersion = when {
+        Build.VERSION.SDK_INT >= 31 ->
+            queryArtMainlineVersion()
+        Build.VERSION.SDK_INT == 30 ->
+            1
+        else ->
+            -1
+    }
+
+    /**
+     * Starting with the first Android U release, ART mainline drops optimizations after method
+     * tracing occurs, so we disable tracing on those mainline versions.
+     *
+     * Fix cherry picked into 341513000, so we exclude that value
+     *
+     * See b/303660864
+     */
+    private val ART_MAINLINE_MIN_VERSIONS_AFFECTING_METHOD_TRACING = 340000000L.until(341513000)
+
+    val methodTracingAffectsMeasurements =
+        Build.VERSION.SDK_INT in 26..30 || // b/313868903
+            artMainlineVersion in ART_MAINLINE_MIN_VERSIONS_AFFECTING_METHOD_TRACING // b/303660864
 }
diff --git a/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/BaselineProfileRuleTest.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/BaselineProfileRuleTest.kt
index 5734d20..f46ad05 100644
--- a/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/BaselineProfileRuleTest.kt
+++ b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/BaselineProfileRuleTest.kt
@@ -43,6 +43,9 @@
 
     @Before
     fun setup() {
+        // Mokey devices seem to behave differently (b/319515652) and the generated profile
+        // doesn't output the class symbol line. This makes the test fail. While we investigate
+        // the scope of the failure, suppress the test on this device
         assumeFalse(isMokeyDevice())
     }
 
diff --git a/browser/browser/build.gradle b/browser/browser/build.gradle
index b06a225..846fc99 100644
--- a/browser/browser/build.gradle
+++ b/browser/browser/build.gradle
@@ -29,7 +29,7 @@
 dependencies {
     api("androidx.core:core:1.1.0")
     api("androidx.annotation:annotation:1.2.0")
-    api(project(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
     api(libs.guavaListenableFuture)
 
     implementation("androidx.collection:collection:1.1.0")
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 261e52e..1bc4c56 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -20,7 +20,6 @@
 import androidx.build.AndroidXImplPlugin.Companion.TASK_TIMEOUT_MINUTES
 import androidx.build.Release.DEFAULT_PUBLISH_CONFIG
 import androidx.build.buildInfo.addCreateLibraryBuildInfoFileTasks
-import androidx.build.checkapi.AndroidMultiplatformApiTaskConfig
 import androidx.build.checkapi.JavaApiTaskConfig
 import androidx.build.checkapi.KmpApiTaskConfig
 import androidx.build.checkapi.LibraryApiTaskConfig
@@ -55,7 +54,6 @@
 import com.android.build.gradle.TestExtension
 import com.android.build.gradle.TestPlugin
 import com.android.build.gradle.TestedExtension
-import com.android.build.gradle.api.KotlinMultiplatformAndroidPlugin
 import com.android.build.gradle.tasks.factory.AndroidUnitTest
 import java.io.File
 import java.time.Duration
@@ -141,17 +139,10 @@
                 is LibraryPlugin -> configureWithLibraryPlugin(project, androidXExtension)
                 is AppPlugin -> configureWithAppPlugin(project, androidXExtension)
                 is TestPlugin -> configureWithTestPlugin(project, androidXExtension)
-                is KotlinMultiplatformAndroidPlugin ->
-                    configureWithKotlinMultiplatformAndroidPlugin(
-                        project,
-                        androidXKmpExtension.agpKmpExtension,
-                        androidXExtension
-                    )
                 is KotlinBasePluginWrapper -> configureWithKotlinPlugin(
                     project,
                     androidXExtension,
-                    plugin,
-                    androidXKmpExtension
+                    plugin
                 )
             }
         }
@@ -160,6 +151,9 @@
         project.configureKtlint()
         project.configureKotlinVersion()
 
+        // Avoid conflicts between full Guava and LF-only Guava.
+        project.configureGuavaUpgradeHandler()
+
         // Configure all Jar-packing tasks for hermetic builds.
         project.tasks.withType(Zip::class.java).configureEach { it.configureForHermeticBuild() }
         project.tasks.withType(Copy::class.java).configureEach { it.configureForHermeticBuild() }
@@ -173,14 +167,16 @@
 
         project.configureTaskTimeouts()
         project.configureMavenArtifactUpload(
-            androidXExtension, androidXKmpExtension, componentFactory) {
-            project.addCreateLibraryBuildInfoFileTasks(androidXExtension)
-        }
+            androidXExtension,
+            androidXKmpExtension,
+            componentFactory
+        )
         project.publishInspectionArtifacts()
         project.configureExternalDependencyLicenseCheck()
         project.configureProjectStructureValidation(androidXExtension)
         project.configureProjectVersionValidation(androidXExtension)
         project.registerProjectOrArtifact()
+        project.addCreateLibraryBuildInfoFileTasks(androidXExtension)
         project.validateMultiplatformPluginHasNotBeenApplied()
 
         project.tasks.register("printCoordinates", PrintProjectCoordinatesTask::class.java) {
@@ -426,8 +422,7 @@
     private fun configureWithKotlinPlugin(
         project: Project,
         androidXExtension: AndroidXExtension,
-        plugin: KotlinBasePluginWrapper,
-        androidXMultiplatformExtension: AndroidXMultiplatformExtension
+        plugin: KotlinBasePluginWrapper
     ) {
         project.configureKtfmt()
 
@@ -487,17 +482,16 @@
         if (plugin is KotlinMultiplatformPluginWrapper) {
             KonanPrebuiltsSetup.configureKonanDirectory(project)
             KmpLinkTaskWorkaround.serializeLinkTasks(project)
-            project.afterEvaluate {
-                val libraryExtension = project.extensions.findByType<LibraryExtension>()
-                if (libraryExtension != null) {
-                    libraryExtension.configureAndroidLibraryWithMultiplatformPluginOptions()
-                } else if (!androidXMultiplatformExtension.hasAndroidMultiplatform()) {
-                    // Kotlin MPP does not apply java plugin anymore, but we still want to configure
-                    // all java-related tasks.
-                    // We only need to do this when project does not have Android plugin, which already
-                    // configures Java tasks.
-                    configureWithJavaPlugin(project, androidXExtension)
-                }
+
+            val libraryExtension = project.extensions.findByType<LibraryExtension>()
+            if (libraryExtension != null) {
+                libraryExtension.configureAndroidLibraryWithMultiplatformPluginOptions()
+            } else {
+                // Kotlin MPP does not apply java plugin anymore, but we still want to configure
+                // all java-related tasks.
+                // We only need to do this when project does not have Android plugin, which already
+                // configures Java tasks.
+                configureWithJavaPlugin(project, androidXExtension)
             }
             project.configureKmp()
             project.configureSourceJarForMultiplatform(androidXExtension)
@@ -554,6 +548,7 @@
         project.addToProjectMap(androidXExtension)
     }
 
+    @Suppress("UNUSED")
     private fun configureWithKotlinMultiplatformAndroidPlugin(
         project: Project,
         kotlinMultiplatformAndroidTarget: KotlinMultiplatformAndroidTarget,
@@ -570,11 +565,6 @@
             project,
             androidXExtension
         )
-
-        project.configureProjectForApiTasks(
-            AndroidMultiplatformApiTaskConfig,
-            androidXExtension
-        )
     }
 
     /**
@@ -992,7 +982,6 @@
         project.extensions.findByType<LibraryAndroidComponentsExtension>()!!.finalizeDsl {
             it.defaultConfig.aarMetadata.minCompileSdk = it.compileSdk
         }
-        project.fixGuavaDeps()
         project.disableStrictVersionConstraints()
         project.setPublishProperty(androidXExtension)
         project.afterEvaluate {
@@ -1006,7 +995,6 @@
     ) {
         // Propagate the compileSdk value into minCompileSdk.
         aarMetadata.minCompileSdk = compileSdk
-        project.fixGuavaDeps()
         project.disableStrictVersionConstraints()
         project.setPublishProperty(androidXExtension)
     }
@@ -1026,7 +1014,12 @@
         }
     }
 
-    private fun Project.fixGuavaDeps() {
+    /**
+     * Adds a module handler replacement rule that treats full Guava (of any version) as an upgrade
+     * to ListenableFuture-only Guava. This prevents irreconcilable versioning conflicts and/or
+     * class duplication issues.
+     */
+    private fun Project.configureGuavaUpgradeHandler() {
         // The full Guava artifact is very large, so they split off a special artifact containing a
         // standalone version of the commonly-used ListenableFuture interface. However, they also
         // structured the artifacts in a way that causes dependency resolution conflicts:
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt b/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
index 64cb59a..8554362 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
@@ -55,8 +55,7 @@
 fun Project.configureMavenArtifactUpload(
     androidXExtension: AndroidXExtension,
     androidXKmpExtension: AndroidXMultiplatformExtension,
-    componentFactory: SoftwareComponentFactory,
-    afterConfigure: () -> Unit
+    componentFactory: SoftwareComponentFactory
 ) {
     apply(mapOf("plugin" to "maven-publish"))
     var registered = false
@@ -66,8 +65,7 @@
                 androidXExtension,
                 androidXKmpExtension,
                 component,
-                componentFactory,
-                afterConfigure
+                componentFactory
             )
             Release.register(this, androidXExtension)
             registered = true
@@ -111,8 +109,7 @@
     extension: AndroidXExtension,
     androidxKmpExtension: AndroidXMultiplatformExtension,
     component: SoftwareComponent,
-    componentFactory: SoftwareComponentFactory,
-    afterConfigure: () -> Unit
+    componentFactory: SoftwareComponentFactory
 ) {
     val androidxGroup = validateCoordinatesAndGetGroup(extension)
     val projectArchiveDir =
@@ -153,13 +150,12 @@
                 }
             } else {
                 if (project.isMultiplatformPublicationEnabled()) {
-                    configureMultiplatformPublication(componentFactory, afterConfigure)
+                    configureMultiplatformPublication(componentFactory)
                 } else {
                     it.create<MavenPublication>("maven") { from(component) }
                     tasks.getByName("publishMavenPublicationToMavenRepository").doFirst {
                         removePreviouslyUploadedArchives(projectArchiveDir)
                     }
-                    afterConfigure()
                 }
             }
         }
@@ -301,10 +297,7 @@
     return extensions.findByType<KotlinMultiplatformExtension>() != null
 }
 
-private fun Project.configureMultiplatformPublication(
-    componentFactory: SoftwareComponentFactory,
-    afterConfigure: () -> Unit
-) {
+private fun Project.configureMultiplatformPublication(componentFactory: SoftwareComponentFactory) {
     val multiplatformExtension = extensions.findByType<KotlinMultiplatformExtension>()!!
 
     multiplatformExtension.targets.all { target ->
@@ -313,17 +306,16 @@
         }
     }
 
-    replaceBaseMultiplatformPublication(componentFactory, afterConfigure)
+    replaceBaseMultiplatformPublication(componentFactory)
 }
 
 /**
- * This was added because KMP did not include a sources configuration (b/235486368), so we replaced
- * it with our own publication that includes it. This can be cleaned up now that the bug is fixed
- * which is tracked here b/309641019
+ * KMP does not include a sources configuration (b/235486368), so we replace it with our own
+ * publication that includes it. This uses internal API as a workaround while waiting for a fix on
+ * the original bug.
  */
 private fun Project.replaceBaseMultiplatformPublication(
-    componentFactory: SoftwareComponentFactory,
-    afterConfigure: () -> Unit
+    componentFactory: SoftwareComponentFactory
 ) {
     val kotlinComponent = components.findByName("kotlin") as SoftwareComponentInternal
     withSourcesComponents(
@@ -369,7 +361,6 @@
             }
 
             disableBaseKmpPublications()
-            afterConfigure()
         }
     }
 }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateLibraryBuildInfoFileTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateLibraryBuildInfoFileTask.kt
index 98819d0..94757ef 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateLibraryBuildInfoFileTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateLibraryBuildInfoFileTask.kt
@@ -248,10 +248,15 @@
             // Unfortunately, dependency information is only available through internal API
             // (See https://github.com/gradle/gradle/issues/21345).
             publications.withType(MavenPublicationInternal::class.java).configureEach { mavenPub ->
-                // java-gradle-plugin creates marker publications that are aliases of the
-                // main publication.  We do not track these aliases.
-                if (!mavenPub.isAlias) {
-                    createTaskForComponent(mavenPub, extension.mavenGroup, mavenPub.artifactId)
+                // Ideally we would be able to inspect each publication after initial configuration
+                // without using afterEvaluate, but there is not a clean gradle API for doing
+                // that (see https://github.com/gradle/gradle/issues/21424)
+                afterEvaluate {
+                    // java-gradle-plugin creates marker publications that are aliases of the
+                    // main publication.  We do not track these aliases.
+                    if (!mavenPub.isAlias) {
+                        createTaskForComponent(mavenPub, extension.mavenGroup, mavenPub.artifactId)
+                    }
                 }
             }
         }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
index f84fbe7..3e8cdbe 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
@@ -41,8 +41,6 @@
 
 object KmpApiTaskConfig : ApiTaskConfig()
 
-object AndroidMultiplatformApiTaskConfig : ApiTaskConfig()
-
 fun AndroidXExtension.shouldConfigureApiTasks(): Boolean {
     if (!project.state.executed) {
         throw GradleException(
@@ -175,10 +173,6 @@
                         .processManifestProvider
                         .get() as ProcessLibraryManifest
             }
-            is AndroidMultiplatformApiTaskConfig -> {
-                javaInputs = JavaCompileInputs.fromKmpAndroidTarget(project)
-                processManifest = null
-            }
             is KmpApiTaskConfig -> {
                 javaInputs = JavaCompileInputs.fromKmpJvmTarget(project)
                 processManifest = null
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/java/JavaCompileInputs.kt b/buildSrc/private/src/main/kotlin/androidx/build/java/JavaCompileInputs.kt
index fb1f4d0..760d0b8 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/java/JavaCompileInputs.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/java/JavaCompileInputs.kt
@@ -18,7 +18,6 @@
 
 import androidx.build.getAndroidJar
 import androidx.build.multiplatformExtension
-import com.android.build.api.dsl.KotlinMultiplatformAndroidTarget
 import java.io.File
 import org.gradle.api.Project
 import org.gradle.api.file.FileCollection
@@ -88,41 +87,6 @@
             )
         }
 
-        /**
-         * Returns the JavaCompileInputs for the `android` target of a KMP project.
-         *
-         * @param project The project whose main android target inputs will be returned.
-         */
-        fun fromKmpAndroidTarget(project: Project): JavaCompileInputs {
-            val kmpExtension =
-                checkNotNull(project.multiplatformExtension) {
-                    """
-                ${project.path} needs to have Kotlin Multiplatform Plugin applied to obtain its
-                android source sets.
-                """
-                        .trimIndent()
-                }
-            val target = kmpExtension.targets.withType(
-                KotlinMultiplatformAndroidTarget::class.java
-            ).single()
-            val sourceCollection =
-                project.files(
-                    project.provider {
-                        target.sourceFiles(
-                            compilationName = KotlinCompilation.MAIN_COMPILATION_NAME
-                        )
-                    }
-                )
-
-            return JavaCompileInputs(
-                sourcePaths = sourceCollection,
-                dependencyClasspath =
-                target.compilations[KotlinCompilation.MAIN_COMPILATION_NAME]
-                    .compileDependencyFiles,
-                bootClasspath = project.getAndroidJar()
-            )
-        }
-
         // Constructs a JavaCompileInputs from a sourceset
         fun fromSourceSet(sourceSet: SourceSet, project: Project): JavaCompileInputs {
             val sourcePaths: FileCollection =
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/license/CheckExternalDependencyLicensesTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/license/CheckExternalDependencyLicensesTask.kt
index 4657e60..e3ec845 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/license/CheckExternalDependencyLicensesTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/license/CheckExternalDependencyLicensesTask.kt
@@ -143,11 +143,6 @@
                                         .filterNot { it.group?.startsWith("com.android") == true }
                                         .filterNot { it.group?.startsWith("android.arch") == true }
                                         .filterNot { it.group?.startsWith("androidx") == true }
-                                        .filterNot {
-                                            // listablefuture conflicts with guava, skip it
-                                            it.group.equals("com.google.guava") &&
-                                                it.name.equals("listenablefuture")
-                                        }
                                 }
                                 .forEach { checkerConfig.dependencies.add(it) }
                         }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
index b289706..59f4b95 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
@@ -166,7 +166,7 @@
     ${TERMINAL_RED}Your change has API compatibility issues. Fix the code according to the messages above.$TERMINAL_RESET
 
     If you *intentionally* want to break compatibility, you can suppress it with
-    ./gradlew ignoreApiChange && ./gradlew updateApi
+    ./gradlew ignoreApiChanges && ./gradlew updateApi
 """
 
 private fun createFrozenCompatibilityCheckError(referenceVersion: String) =
@@ -174,5 +174,5 @@
     ${TERMINAL_RED}The API surface was finalized in $referenceVersion. Revert the changes noted in the errors above.$TERMINAL_RESET
 
     If you have obtained permission from Android API Council or Jetpack Working Group to bypass this policy, you can suppress this check with:
-    ./gradlew ignoreApiChange && ./gradlew updateApi
+    ./gradlew ignoreApiChanges && ./gradlew updateApi
 """
diff --git a/busytown/androidx_with_metalava.sh b/busytown/androidx_with_metalava.sh
index 56a40a6..fa03981 100755
--- a/busytown/androidx_with_metalava.sh
+++ b/busytown/androidx_with_metalava.sh
@@ -6,9 +6,19 @@
 # while landing Metalava w/ breaking API changes
 METALAVA_INTEGRATION_ENFORCED=true
 
+# The default targets to build if no arguments
+# are provided on the command line.
+DEFAULT_TARGETS=" \
+  listTaskOutputs \
+  checkApi \
+  "
+
 if $METALAVA_INTEGRATION_ENFORCED
 then
-    $SCRIPT_PATH/impl/build-metalava-and-androidx.sh \
-        listTaskOutputs \
-        checkApi
+  # If no arguments are provided on the command line
+  # then use the defaults otherwise pass the command
+  # line arguments through.
+  $SCRIPT_PATH/impl/build-metalava-and-androidx.sh \
+    ${1:-$DEFAULT_TARGETS} \
+    "${@:2}"
 fi
diff --git a/camera/camera-camera2-pipe-integration/build.gradle b/camera/camera-camera2-pipe-integration/build.gradle
index 1818b1c..8d44786 100644
--- a/camera/camera-camera2-pipe-integration/build.gradle
+++ b/camera/camera-camera2-pipe-integration/build.gradle
@@ -74,7 +74,7 @@
     androidTestImplementation(libs.kotlinCoroutinesAndroid)
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.truth)
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
     androidTestImplementation(project(":camera:camera-lifecycle"))
     androidTestImplementation(project(":camera:camera-testing")) {
         // Ensure camera-testing does not pull in androidx.test dependencies
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EncoderProfilesProviderAdapterDeviceTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EncoderProfilesProviderAdapterDeviceTest.kt
index cf45bda..dee4ab9 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EncoderProfilesProviderAdapterDeviceTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/EncoderProfilesProviderAdapterDeviceTest.kt
@@ -32,6 +32,7 @@
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_8
 import androidx.camera.testing.impl.CameraUtil
+import androidx.camera.testing.impl.LabTestRule
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
@@ -77,6 +78,9 @@
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest()
 
+    @get:Rule
+    val labTestRule = LabTestRule()
+
     @Before
     fun setup() {
         Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK))
@@ -138,6 +142,7 @@
 
         val profiles = CamcorderProfile.getAll(cameraId, quality)
         val video = profiles!!.videoProfiles[0]
+        Assume.assumeTrue(video != null)
         val audio = profiles.audioProfiles[0]
         val profilesProxy = encoderProfilesProvider.getAll(quality)
         val videoProxy = profilesProxy!!.videoProfiles[0]
@@ -167,10 +172,10 @@
     @Test
     fun afterApi33_hasSameContentAsEncoderProfiles() {
         Assume.assumeTrue(encoderProfilesProvider.hasProfile(quality))
-        skipTestOnDevicesWithProblematicBuild()
 
         val profiles = CamcorderProfile.getAll(cameraId, quality)
         val video = profiles!!.videoProfiles[0]
+        Assume.assumeTrue(video != null)
         val audio = profiles.audioProfiles[0]
         val profilesProxy = encoderProfilesProvider.getAll(quality)
         val videoProxy = profilesProxy!!.videoProfiles[0]
@@ -196,6 +201,16 @@
         assertThat(audioProxy.profile).isEqualTo(audio.profile)
     }
 
+    @LabTestRule.LabTestOnly
+    @SdkSuppress(minSdkVersion = 31)
+    @Test
+    fun detectNullVideoProfile() {
+        Assume.assumeTrue(CamcorderProfile.hasProfile(intCameraId, quality))
+        skipTestOnDevicesWithProblematicBuild()
+        val profiles = CamcorderProfile.getAll(cameraId, quality)!!
+        assertThat(profiles.videoProfiles[0]).isNotNull()
+    }
+
     private fun skipTestOnDevicesWithProblematicBuild() {
         // Skip test for b/265613005, b/223439995 and b/277174217
         val hasVideoProfilesQuirk = DeviceQuirks[InvalidVideoProfilesQuirk::class.java] != null
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
new file mode 100644
index 0000000..0916fffc
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.adapter
+
+import android.content.Context
+import android.hardware.camera2.CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
+import androidx.camera.camera2.pipe.integration.CameraPipeConfig
+import androidx.camera.camera2.pipe.testing.toCameraInfoAdapter
+import androidx.camera.core.CameraSelector
+import androidx.camera.testing.impl.CameraUtil
+import androidx.camera.testing.impl.CameraXUtil
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Contains tests for [CameraInfoAdapter].
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 21)
+class CameraInfoAdapterTest {
+
+    @get:Rule
+    val useCamera = CameraUtil.grantCameraPermissionAndPreTest()
+
+    private val lensFacing = CameraSelector.LENS_FACING_BACK
+    private lateinit var cameraInfoAdapter: CameraInfoAdapter
+
+    @Before
+    fun setUp() {
+        val context: Context = getApplicationContext()
+        CameraXUtil.initialize(context, CameraPipeConfig.defaultConfig())
+        val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
+        val camera = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
+        cameraInfoAdapter = camera.cameraInfo.toCameraInfoAdapter()
+    }
+
+    @Test
+    fun canReturnSupportedOutputFormats() {
+        val formats = cameraInfoAdapter.supportedOutputFormats.toList()
+        val cameraCharacteristics = CameraUtil.getCameraCharacteristics(lensFacing)!!
+        val streamConfigurationMap = cameraCharacteristics.get(SCALER_STREAM_CONFIGURATION_MAP)!!
+
+        assertThat(formats).containsExactlyElementsIn(streamConfigurationMap.outputFormats.toList())
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatTest.kt
new file mode 100644
index 0000000..df0e65b
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat
+
+import android.content.Context
+import android.hardware.camera2.CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
+import android.hardware.camera2.params.StreamConfigurationMap
+import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraPipe
+import androidx.camera.camera2.pipe.integration.CameraPipeConfig
+import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
+import androidx.camera.core.CameraSelector
+import androidx.camera.testing.impl.CameraUtil
+import androidx.camera.testing.impl.CameraXUtil
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Contains tests for [StreamConfigurationMapCompat].
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 21)
+class StreamConfigurationMapCompatTest {
+
+    @get:Rule
+    val useCamera = CameraUtil.grantCameraPermissionAndPreTest()
+
+    private val lensFacing = CameraSelector.LENS_FACING_BACK
+    private lateinit var streamConfigurationMap: StreamConfigurationMap
+    private lateinit var streamConfigurationMapCompat: StreamConfigurationMapCompat
+
+    @Before
+    fun setUp() {
+        val context: Context = getApplicationContext()
+        CameraXUtil.initialize(context, CameraPipeConfig.defaultConfig())
+        val cameraId = CameraUtil.getCameraIdWithLensFacing(lensFacing)!!
+        val cameraPipe = CameraPipe(CameraPipe.Config(context))
+        val cameraMetadata = cameraPipe.cameras().awaitCameraMetadata(CameraId(cameraId))!!
+        streamConfigurationMap = cameraMetadata[SCALER_STREAM_CONFIGURATION_MAP]!!
+        streamConfigurationMapCompat = StreamConfigurationMapCompat(
+            streamConfigurationMap,
+            OutputSizesCorrector(cameraMetadata, streamConfigurationMap)
+        )
+    }
+
+    @Test
+    fun canGetOutputFormats() {
+        val formats = streamConfigurationMapCompat.getOutputFormats()!!.toList()
+        assertThat(formats).containsExactlyElementsIn(streamConfigurationMap.outputFormats.toList())
+    }
+}
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 8c54f5d..b2a2064 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
@@ -184,6 +184,10 @@
         }
     }
 
+    override fun getSupportedOutputFormats(): Set<Int> {
+        return streamConfigurationMapCompat.getOutputFormats()?.toSet() ?: emptySet()
+    }
+
     @SuppressLint("ClassVerificationFailure")
     override fun getSupportedResolutions(format: Int): List<Size> {
         return streamConfigurationMapCompat.getOutputSizes(format)?.toList() ?: emptyList()
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompat.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompat.kt
index 00ebe44..03c96f1 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompat.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompat.kt
@@ -16,6 +16,8 @@
 
 package androidx.camera.camera2.pipe.integration.compat
 
+import android.graphics.ImageFormat
+import android.graphics.PixelFormat
 import android.hardware.camera2.params.StreamConfigurationMap
 import android.os.Build
 import android.util.Size
@@ -53,6 +55,20 @@
     }
 
     /**
+     * Get the image format output formats in this stream configuration.
+     *
+     * All image formats returned by this function will be defined in either ImageFormat or in
+     * PixelFormat.
+     *
+     * @return an array of integer format
+     * @see [ImageFormat]
+     * @see [PixelFormat]
+     */
+    fun getOutputFormats(): Array<Int>? {
+        return impl.getOutputFormats()
+    }
+
+    /**
      * Get a list of sizes compatible with the requested image `format`.
      *
      *
@@ -148,6 +164,7 @@
     }
 
     internal interface StreamConfigurationMapCompatImpl {
+        fun getOutputFormats(): Array<Int>?
         fun getOutputSizes(format: Int): Array<Size>?
         fun <T> getOutputSizes(klass: Class<T>): Array<Size>?
         fun getHighResolutionOutputSizes(format: Int): Array<Size>?
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatBaseImpl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatBaseImpl.kt
index fef2df7..918f8ed 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatBaseImpl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatBaseImpl.kt
@@ -27,6 +27,11 @@
     val streamConfigurationMap: StreamConfigurationMap?
 ) :
     StreamConfigurationMapCompat.StreamConfigurationMapCompatImpl {
+
+    override fun getOutputFormats(): Array<Int>? {
+        return streamConfigurationMap?.outputFormats?.toTypedArray()
+    }
+
     override fun getOutputSizes(format: Int): Array<Size>? {
         val sizes: Array<Size> =
             if (format == ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE) {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
index 7df4b6c..2721ccf 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
@@ -28,14 +28,14 @@
  * Quirk denoting the video profile list returns by [EncoderProfiles] is invalid.
  *
  * QuirkSummary
- * - Bug Id: 267727595, 278860860, 298951126, 298952500
+ * - Bug Id: 267727595, 278860860, 298951126, 298952500, 320747756
  * - Description: When using [EncoderProfiles] on some builds of Android API 33,
  *   [EncoderProfiles.getVideoProfiles] returns a list with size one, but the single value in the
  *   list is null. This is not the expected behavior, and makes [EncoderProfiles] lack of video
  *   information.
  * - Device(s): Pixel 4 and above pixel devices with TP1A or TD1A builds (API 33), Samsung devices
- *              with TP1A build (API 33), Xiaomi devices with TKQ1 build (API 33), OnePlus and Oppo
- *              devices with API 33 build.
+ *              with TP1A build (API 33), Xiaomi devices with TKQ1/TP1A build (API 33), OnePlus and
+ *              Oppo devices with API 33 build.
  *
  * TODO: enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
@@ -64,7 +64,8 @@
 
         private val AFFECTED_OPPO_MODELS: List<String> = listOf(
             "cph2437",
-            "cph2525"
+            "cph2525",
+            "pht110",
         )
 
         fun isEnabled(): Boolean {
@@ -82,7 +83,7 @@
 
         private fun isAffectedXiaomiDevices(): Boolean {
             return ("redmi".equals(Build.BRAND, true) || "xiaomi".equals(Build.BRAND, true)) &&
-                isTkq1Build()
+                (isTkq1Build() || isTp1aBuild())
         }
 
         private fun isAffectedOnePlusDevices(): Boolean {
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 72c5146..b50bd25 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
@@ -187,6 +187,10 @@
                 throw NotImplementedError("Not used in testing")
             }
 
+            override fun getSupportedOutputFormats(): MutableSet<Int> {
+                throw NotImplementedError("Not used in testing")
+            }
+
             override fun isPreviewStabilizationSupported(): Boolean {
                 throw NotImplementedError("Not used in testing")
             }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeSurfaces.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeSurfaces.kt
index dc8abe3..61d2f6d 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeSurfaces.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeSurfaces.kt
@@ -31,7 +31,7 @@
 @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 class FakeSurfaces : AutoCloseable {
     private val fakeSurfaces = mutableListOf<Surface>()
-    fun createFakeSurface(size: Size): Surface {
+    fun createFakeSurface(size: Size = Size(640, 480)): Surface {
         val surface = create(size)
         synchronized(fakeSurfaces) {
             fakeSurfaces.add(surface)
@@ -51,7 +51,7 @@
     companion object {
         private val fakeSurfaceTextureNames = atomic(0)
 
-        fun create(size: Size): Surface {
+        fun create(size: Size = Size(640, 480)): Surface {
             return Surface(
                 SurfaceTexture(fakeSurfaceTextureNames.getAndIncrement()).also {
                     it.setDefaultBufferSize(size.width, size.height)
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Frame.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Frame.kt
new file mode 100644
index 0000000..3e52406
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Frame.kt
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.camera.camera2.pipe.FrameReference.Companion.acquire
+import androidx.camera.camera2.pipe.media.OutputImage
+
+/**
+ * A [Frame] is a container for all of the data and outputs that are sent to, and produced from, a
+ * single request issued to the camera.
+ *
+ * A frame represents a single "exposure" and/or moment in time. Since many modern cameras operate
+ * multiple individual sub-cameras together as a larger "logical" camera, this means that the
+ * outputs produced by the frame may contain outputs from more than one individual sub camera.
+ *
+ * Frames allow a developer to reason about the outputs from the camera without having to do all of
+ * the timestamp correlation and internal error handling. In the simple case, a frame will have the
+ * original request, the fully resolved [RequestMetadata] (which includes any modifications due to
+ * required parameters, 3A state, etc), a unique FrameId, the camera provided timestamp, and
+ * accessors for getting images when they are available. Frames are created as soon as the camera
+ * indicates an exposure has started, and output images may not be immediately available.
+ *
+ * Since a Frame holds onto expensive objects (Images) it is very important to make sure each frame
+ * is ALWAYS closed as soon as it is no longer needed. Cameras can easily operate at 30-60 frames
+ * per second.
+ *
+ * Implementations of this interface are thread safe.
+ *
+ * **Warning**: All [AutoCloseable] resources, including the [Frame] itself, must be closed or it
+ * will result in resource leaks and/or camera stalls!
+ *
+ * Example:
+ * ```
+ * /** Process and save the jpeg output from a Frame */
+ * suspend fun processAndSaveFrame(frame: Frame): Boolean {
+ *     var jpegImage: OutputImage? = null
+ *     var frameMetadata: FrameMetadata? = null
+ *
+ *     frame.use {
+ *         jpegImage = frame[jpegStreamId].await()
+ *         frameInfo = frame.frameInfo.await()?.metadata
+ *     } // `frame` is closed here. jpegImage is not.
+ *
+ *
+ *     if (jpegImage == null || frameMetadata == null) {
+ *         jpegImage?.close() // Always close the image.
+ *         return false
+ *     }
+ *
+ *     // save is responsible for closing jpegImage
+ *     return save(jpegImage, frameMetadata)
+ * }
+ * ```
+ */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+interface Frame : FrameReference, AutoCloseable {
+    /**
+     * Return the [FrameInfo], if available or suspend until the FrameInfo has been resolved.
+     *
+     * Returns null if the frameInfo could not be produced for any reason, or if the frame is
+     * closed. If frameInfo is not available, [frameInfoStatus] can be used to understand why this
+     * metadata is not available.
+     */
+    suspend fun awaitFrameInfo(): FrameInfo?
+
+    /**
+     * Return the [FrameInfo], if available, for this Frame. This method does not block and will
+     * return null if the Frame has been closed, or if the [FrameInfo] has not yet been produced.
+     */
+    fun getFrameInfo(): FrameInfo?
+
+    /**
+     * Return the [OutputImage] for this [streamId], if available or suspend until the output for
+     * this stream has been resolved.
+     *
+     * Returns null if the image could not be produced for any reason, or if this frame is closed.
+     * If an image is not available, [imageStatus] can be used to understand the reason this image
+     * was not produced by the camera. Each call produces a unique [OutputImage] that *must* be
+     * closed to avoid memory leaks.
+     */
+    suspend fun awaitImage(streamId: StreamId): OutputImage?
+
+    /**
+     * Return the [OutputImage] for this [streamId], if available.
+     *
+     * Returns null if the image could not be produced for any reason, or if this frame is closed.
+     * If an image is not available, [imageStatus] can be used to understand the reason this image
+     * was not produced by the camera. Each call produces a unique [OutputImage] that *must* be
+     * closed to avoid memory leaks.
+     */
+    fun getImage(streamId: StreamId): OutputImage?
+
+    /**
+     * Listener for non-coroutine based applications that may need to be notified when the state
+     * of this [Frame] changes.
+     */
+    fun addListener(listener: Listener)
+
+    /** Listener for events about an [Frame] */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    interface Listener {
+        /**
+         * Invoked after an [Frame] has been created and has started.
+         *
+         * @param frameNumber is the camera-provided identifier for this Frame.
+         * @param frameTimestamp is the primary camera-provided timestamp for this Frame.
+         */
+        fun onFrameStarted(
+            frameNumber: FrameNumber,
+            frameTimestamp: CameraTimestamp
+        )
+
+        /**
+         * Invoked after [FrameInfo] is available, or has failed to be produced.
+         */
+        fun onFrameInfoAvailable()
+
+        /**
+         * Invoked after the output for a given [StreamId] has been produced.
+         */
+        fun onImageAvailable(streamId: StreamId)
+
+        /**
+         * Invoked after *all* outputs for this [Frame] have been produced. This method will
+         * be invoked after [onImageAvailable] has been invoked for all relevant streams, and will
+         * be invoked immediately after [onFrameStarted] for frames that do not produce outputs.
+         */
+        fun onImagesAvailable()
+
+        /**
+         * Invoked after the [FrameInfo] and all outputs have been completed for this [Frame].
+         */
+        fun onFrameComplete()
+    }
+
+    companion object {
+        val Frame.request
+            get() = this.requestMetadata.request
+
+        val FrameReference.isFrameInfoAvailable
+            get() = this.frameInfoStatus == OutputStatus.AVAILABLE
+
+        fun FrameReference.isImageAvailable(streamId: StreamId) =
+            this.imageStatus(streamId) == OutputStatus.AVAILABLE
+    }
+}
+
+/**
+ * A [FrameId] a unique identifier that represents the order a [Frame] was produced in.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@JvmInline
+value class FrameId(val value: Long)
+
+/**
+ * Represents the status of an output from the camera.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@JvmInline
+value class OutputStatus(val value: Int) {
+    companion object {
+        /** Output is not yet available. */
+        val PENDING = OutputStatus(0)
+
+        /** Output has arrived and is available. */
+        val AVAILABLE = OutputStatus(1)
+
+        /**
+         * Output has been resolved, and is not available for some reason that is not due to Camera
+         * operation, error, or other internal behavior. For example, if the object holding an
+         * output is closed, the method to get the output may return [UNAVAILABLE].
+         */
+        val UNAVAILABLE = OutputStatus(2)
+
+        /** Output is not available because the Camera reported an error for this output. */
+        val ERROR_OUTPUT_FAILED = OutputStatus(10)
+
+        /** Output is not available because it was intentionally aborted, or arrived after close. */
+        val ERROR_OUTPUT_ABORTED = OutputStatus(11)
+
+        /**
+         * Output is not available because it was unexpectedly dropped or failed to arrive from the
+         * camera without some other kind of explicit error.
+         */
+        val ERROR_OUTPUT_MISSING = OutputStatus(12)
+
+        /**
+         * Output is not available because it was intentionally dropped due to rate limiting. This
+         * can happen when the configured output capacity has been exceeded. While this can happen
+         * under normal usage, it can also indicate that some bit of code is not correctly closing
+         * frames and/or images.
+         */
+        val ERROR_OUTPUT_DROPPED = OutputStatus(13)
+    }
+}
+
+/**
+ * A FrameCapture represents a [Request] that has been sent to the Camera, but that has not yet
+ * started. This object serves as a placeholder until the Camera begins exposing the frame, at
+ * which point all interactions should happen on the provided [Frame].
+ *
+ * Closing this FrameCapture will *not* cancel or abort the [Request].
+ *
+ * **Warning**: This object *must* must be closed or it will result in resource leaks and/or camera
+ * stalls!
+ *
+ * Example:
+ *
+ * ```
+ * /** Capture, process, and save a jpeg from the camera.  */
+ * suspend fun captureFrame(cameraGraphSession: CameraGraph.Session): Boolean {
+ *     // Issue the request to the camera and return a deferred capture.
+ *     val frameCapture = cameraGraphSession.capture(
+ *         Request(
+ *             streams = listOf(viewfinderStream, jpegStream)
+ *         )
+ *     )
+ *
+ *     // Wait for the frame to start and then pass it to `processAndSaveFrame`
+ *     return frameCapture.use {
+ *         val frame = frameCapture.awaitFrame() // suspend
+ *         frameCapture.close() // close the frameCapture early since we have the Frame
+ *         if (frame != null) {
+ *             processAndSaveFrame(frame) // responsible for closing frame
+ *         } else {
+ *             false // capture failed
+ *         }
+ *     } // .use causes frameCapture to close, even if there is an exception
+ * }
+ * ```
+ */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+interface FrameCapture : AutoCloseable {
+    /**
+     * The [Request] that was used to issue this [FrameCapture].
+     */
+    val request: Request
+
+    /**
+     * Get the status of the pending [Frame].
+     */
+    val frameStatus: OutputStatus
+
+    /**
+     * Get or suspend until the [Frame] that will be produced by the camera for this [request] is
+     * available, failed, or aborted, or until this object is closed.
+     *
+     * Invoking this multiple times will produce distinct Frame instances that will need to be
+     * individually closed.
+     */
+    suspend fun awaitFrame(): Frame?
+
+    /**
+     * Get the [Frame] that will was produced by the camera for this [request] or null if the
+     * request failed, was aborted, or if this [FrameCapture] was closed.
+     *
+     * Invoking this multiple times will produce distinct Frame instances that will need to be
+     * individually closed.
+     */
+    fun getFrame(): Frame?
+
+    /**
+     * Adds a [Frame.Listener] that will be invoked for each of the subsequent [Frame] events.
+     */
+    fun addListener(listener: Frame.Listener)
+}
+
+/**
+ * A FrameReference is a weak reference to a [Frame]. It will not prevent the underlying frame
+ * from being closed or released unless the frame is acquired via [acquire] or [tryAcquire].
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+interface FrameReference {
+    /**
+     * Metadata about the request that produced this [Frame].
+     *
+     * [RequestMetadata] includes any modifications to the original request that were made due to
+     * 3A, Zoom, default and required parameters defined, and more.
+     */
+    val requestMetadata: RequestMetadata
+
+    /**
+     * The unique, sequential identifier defined by CameraPipe for this Frame. This identifier is
+     * incremented each time a new exposure starts from the Camera.
+     */
+    val frameId: FrameId
+
+    /** The original camera provided [FrameNumber] from this [Frame] */
+    val frameNumber: FrameNumber
+
+    /** The original camera provided [CameraTimestamp] from this [Frame] */
+    val frameTimestamp: CameraTimestamp
+
+    /**
+     * Get the current [OutputStatus] for the FrameInfo of this Frame.
+     */
+    val frameInfoStatus: OutputStatus
+
+    /**
+     * Get the current [OutputStatus] of the output for a given [streamId].
+     */
+    fun imageStatus(streamId: StreamId): OutputStatus
+
+    /**
+     * [StreamId]'s that can be used to access [OutputImage]s from this [Frame] via [Frame.getImage]
+     *
+     * **This may be different from the list of streams defined in the original [Request]!** since
+     * this list will only include streams that were internally created and managed by CameraPipe.
+     */
+    val imageStreams: Set<StreamId>
+
+    /**
+     * Acquire a reference to a [Frame] that can be independently managed or closed. A filter can
+     * be provided to limit which outputs are available.
+     */
+    fun tryAcquire(streamFilter: Set<StreamId>? = null): Frame?
+
+    companion object {
+        /**
+         * Acquire a [Frame] from a [FrameReference]. The outputs can be limited by specifying a
+         * filter to restrict which outputs are acquired.
+         */
+        fun FrameReference.acquire(streamFilter: Set<StreamId>? = null): Frame {
+            return checkNotNull(tryAcquire(streamFilter)) {
+                "Failed to acquire a strong reference to $this!"
+            }
+        }
+    }
+}
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 344c276..903a52f 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
@@ -411,6 +411,8 @@
             }
             if (postviewOutput == null && outputConfig.streams == postviewStream.outputs) {
                 postviewOutput = output
+            } else {
+                allOutputs.add(output)
             }
         } else {
             allOutputs.add(output)
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/StreamGraphImpl.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/StreamGraphImpl.kt
index e608af8..de2945f 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/StreamGraphImpl.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/StreamGraphImpl.kt
@@ -80,7 +80,7 @@
         // Compute groupNumbers for buffer sharing.
         val groupNumbers = mutableMapOf<CameraStream.Config, Int>()
         for (group in graphConfig.exclusiveStreamGroups) {
-            check(group.size > 1)
+            check(group.isNotEmpty())
             val surfaceGroupId = computeNextSurfaceGroupId(graphConfig)
             for (config in group) {
                 check(!groupNumbers.containsKey(config))
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/FrameImpl.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/FrameImpl.kt
new file mode 100644
index 0000000..3408acb
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/FrameImpl.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.internal
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraTimestamp
+import androidx.camera.camera2.pipe.Frame
+import androidx.camera.camera2.pipe.FrameId
+import androidx.camera.camera2.pipe.FrameInfo
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.OutputStatus
+import androidx.camera.camera2.pipe.RequestMetadata
+import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.core.Log
+import androidx.camera.camera2.pipe.media.OutputImage
+import kotlinx.atomicfu.atomic
+
+/**
+ * FrameImpl is the canonical implementation of a [Frame].
+ *
+ * Each instance is closeable, and references to an underlying [FrameState] object which aggregates
+ * and all of the underlying placeholder objects for each expected output.
+ */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+internal class FrameImpl private constructor(
+    private val frameState: FrameState,
+    override val imageStreams: Set<StreamId>,
+) : Frame {
+    internal constructor(frameState: FrameState) : this(
+        frameState, frameState.imageOutputs.map { it.streamId }.toSet()
+    )
+
+    private val closed = atomic(false)
+
+    override fun tryAcquire(streamFilter: Set<StreamId>?): FrameImpl? {
+        if (closed.value) return null
+        if (!frameState.frameInfoOutput.increment()) return null
+
+        var success = true
+        val availableImageStreams = buildSet {
+            for (streamResult in frameState.imageOutputs) {
+                val id = streamResult.streamId
+                if (imageStreams.contains(id) &&
+                    (streamFilter == null || streamFilter.contains(id))
+                ) {
+                    // Increment the reference count for the underlying image output. If successful,
+                    // add it to the list of available streams.
+                    if (streamResult.increment()) {
+                        add(id)
+                    } else {
+                        success = false
+                        break // Exit the loop early if we fail to increment
+                    }
+                }
+            }
+        }
+
+        if (!success) {
+            // Undo the reference counting:
+            // 1. Undo the frameInfoOutput
+            // 2. Undo each successfully incremented output, which is added to availableStreams.
+            frameState.frameInfoOutput.decrement()
+            for (streamResult in frameState.imageOutputs) {
+                if (availableImageStreams.contains(streamResult.streamId)) {
+                    streamResult.decrement()
+                }
+            }
+            return null
+        }
+
+        // Return the new Frame instance
+        return FrameImpl(frameState, availableImageStreams)
+    }
+
+    override fun close() {
+        release()
+    }
+
+    private fun release(): Boolean {
+        if (closed.compareAndSet(expect = false, update = true)) {
+            // This is guaranteed to run *exactly* once no matter how many times release is called.
+            frameState.frameInfoOutput.decrement()
+
+            // Iterate through each of the image outputs and decrement the count for the
+            // imageStreams that are held by this Frame.
+            for (i in frameState.imageOutputs.indices) {
+                val streamResult = frameState.imageOutputs[i]
+                if (imageStreams.contains(streamResult.streamId)) {
+                    streamResult.decrement()
+                }
+            }
+            return true
+        }
+        return false
+    }
+
+    override val requestMetadata: RequestMetadata
+        get() = frameState.requestMetadata
+    override val frameId: FrameId
+        get() = frameState.frameId
+    override val frameNumber: FrameNumber
+        get() = frameState.frameNumber
+    override val frameTimestamp: CameraTimestamp
+        get() = frameState.frameTimestamp
+    override val frameInfoStatus: OutputStatus
+        get() {
+            if (closed.value) return OutputStatus.UNAVAILABLE
+            return frameState.frameInfoOutput.status
+        }
+
+    protected fun finalize() {
+        // https://kotlinlang.org/docs/java-interop.html#finalize
+        // Frames that are no longer reachable should be closed to avoid memory leaks.
+        if (release()) {
+            Log.error {
+                "Failed to close $this! This indicates a memory leak and could cause the camera" +
+                    " to stall, or images to be lost."
+            }
+        }
+    }
+
+    override suspend fun awaitFrameInfo(): FrameInfo? {
+        if (closed.value) return null
+        return frameState.frameInfoOutput.await()
+    }
+
+    override fun getFrameInfo(): FrameInfo? {
+        if (closed.value) return null
+        return frameState.frameInfoOutput.getCompletedOrNull()
+    }
+
+    override suspend fun awaitImage(streamId: StreamId): OutputImage? {
+        if (closed.value) return null
+        if (!imageStreams.contains(streamId)) return null
+        val output = frameState.imageOutputs.firstOrNull { it.streamId == streamId }
+        return output?.await()
+    }
+
+    override fun getImage(streamId: StreamId): OutputImage? {
+        if (closed.value) return null
+        if (!imageStreams.contains(streamId)) return null
+        val output = frameState.imageOutputs.firstOrNull { it.streamId == streamId }
+        return output?.getCompletedOrNull()
+    }
+
+    override fun imageStatus(streamId: StreamId): OutputStatus {
+        if (closed.value || !imageStreams.contains(streamId)) return OutputStatus.UNAVAILABLE
+        return frameState.imageOutputs.firstOrNull { it.streamId == streamId }?.status
+            ?: OutputStatus.UNAVAILABLE
+    }
+
+    override fun addListener(listener: Frame.Listener) {
+        check(!closed.value) { "Cannot add Frame.Listener, $this is closed!" }
+        frameState.addListener(listener)
+    }
+
+    override fun toString(): String = frameState.toString()
+
+    companion object {
+        private val frameIds = atomic(0L)
+        internal fun nextFrameId(): FrameId = FrameId(frameIds.incrementAndGet())
+    }
+}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/FrameState.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/FrameState.kt
new file mode 100644
index 0000000..ba4603a
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/FrameState.kt
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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 android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraTimestamp
+import androidx.camera.camera2.pipe.Frame
+import androidx.camera.camera2.pipe.FrameId
+import androidx.camera.camera2.pipe.FrameInfo
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.OutputStatus
+import androidx.camera.camera2.pipe.RequestMetadata
+import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.internal.FrameState.State.COMPLETE
+import androidx.camera.camera2.pipe.internal.FrameState.State.FRAME_INFO_COMPLETE
+import androidx.camera.camera2.pipe.internal.FrameState.State.STARTED
+import androidx.camera.camera2.pipe.internal.FrameState.State.STREAM_RESULTS_COMPLETE
+import androidx.camera.camera2.pipe.media.OutputDistributor
+import androidx.camera.camera2.pipe.media.OutputImage
+import androidx.camera.camera2.pipe.media.SharedOutputImage
+import java.util.concurrent.CopyOnWriteArrayList
+import kotlinx.atomicfu.atomic
+import kotlinx.atomicfu.updateAndGet
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/**
+ * This class represents a successfully started frame from the camera, and placeholders for the
+ * images and metadata ([FrameInfo]) that we expect the camera to produce.
+ */
+@RequiresApi(21)
+internal class FrameState(
+    val requestMetadata: RequestMetadata,
+    val frameId: FrameId,
+    val frameNumber: FrameNumber,
+    val frameTimestamp: CameraTimestamp,
+    imageStreams: Set<StreamId>
+) {
+    val frameInfoOutput: FrameInfoOutput = FrameInfoOutput()
+    val imageOutputs: List<ImageOutput> = buildList {
+        for (streamId in requestMetadata.streams.keys) {
+            // Only create StreamResult's for streams that this OutputFrameDistributor supports.
+            if (imageStreams.contains(streamId)) {
+                val imageOutput = ImageOutput(streamId)
+                add(imageOutput)
+            }
+        }
+    }
+
+    /**
+     * State always begins in the [STARTED] state, and ends in the [COMPLETE] state. There are three
+     * paths to get there, and is used to control when certain methods on the listener are invoked.
+     *
+     * [STARTED] -> [COMPLETE]
+     * [STARTED] -> [FRAME_INFO_COMPLETE] -> [COMPLETE]
+     * [STARTED] -> [STREAM_RESULTS_COMPLETE] -> [COMPLETE]
+     */
+    private enum class State {
+        STARTED,
+        FRAME_INFO_COMPLETE,
+        STREAM_RESULTS_COMPLETE,
+        COMPLETE
+    }
+
+    private val state = atomic(STARTED)
+    private val streamResultCount = atomic(0)
+    private val outputFrameListeners = CopyOnWriteArrayList<Frame.Listener>()
+
+    fun addListener(listener: Frame.Listener) {
+        listener.onFrameStarted(frameNumber, frameTimestamp)
+
+        // Note: This operation is safe since the outputFrameListeners is a CopyOnWriteArrayList.
+        outputFrameListeners.add(listener)
+    }
+
+    fun onFrameInfoComplete() {
+        // Invoke the onOutputResultsAvailable onOutputMetadataAvailable.
+        for (i in outputFrameListeners.indices) {
+            outputFrameListeners[i].onFrameInfoAvailable()
+        }
+
+        val state = state.updateAndGet { current ->
+            when (current) {
+                STARTED -> FRAME_INFO_COMPLETE
+                STREAM_RESULTS_COMPLETE -> COMPLETE
+                else -> throw IllegalStateException(
+                    "Unexpected frame state for $this! State is $current "
+                )
+            }
+        }
+
+        if (state == COMPLETE) {
+            invokeOnFrameComplete()
+        }
+    }
+
+    fun onStreamResultComplete(streamId: StreamId) {
+        val allResultsCompleted = streamResultCount.incrementAndGet() != imageOutputs.size
+
+        // Invoke the onOutputResultsAvailable listener.
+        for (i in outputFrameListeners.indices) {
+            outputFrameListeners[i].onImageAvailable(streamId)
+        }
+
+        if (allResultsCompleted) return
+
+        // Invoke the onOutputResultsAvailable listener.
+        for (i in outputFrameListeners.indices) {
+            outputFrameListeners[i].onImagesAvailable()
+        }
+
+        val state = state.updateAndGet { current ->
+            when (current) {
+                STARTED -> STREAM_RESULTS_COMPLETE
+                FRAME_INFO_COMPLETE -> COMPLETE
+                else -> throw IllegalStateException(
+                    "Unexpected frame state for $this! State is $current "
+                )
+            }
+        }
+
+        if (state == COMPLETE) {
+            invokeOnFrameComplete()
+        }
+    }
+
+    private fun invokeOnFrameComplete() {
+        // Invoke the onOutputResultsAvailable listener.
+        for (i in outputFrameListeners.indices) {
+            outputFrameListeners[i].onFrameComplete()
+        }
+    }
+
+    override fun toString(): String = "Frame-$frameId(${frameNumber.value}@${frameTimestamp.value})"
+
+    /**
+     * [FrameOutput] handles the logic and reference counting that is required to safely handle a
+     * shared `CompletableDeferred` instance that may contain an expensive closable resource.
+     */
+    @OptIn(ExperimentalCoroutinesApi::class)
+    internal abstract class FrameOutput<T : Any> {
+        private val count = atomic(1)
+
+        /**
+         * To avoid holding onto multiple status objects, this Deferred will hold *either* an
+         * object of type T OR the [OutputStatus] that was passed down when this output was
+         * completed.
+         */
+        val result = CompletableDeferred<Any>()
+
+        fun increment(): Boolean {
+            val current =
+                count.updateAndGet { current ->
+                    if (current <= 0) {
+                        0
+                    } else {
+                        current + 1
+                    }
+                }
+            return current != 0
+        }
+
+        fun decrement() {
+            if (count.decrementAndGet() == 0) {
+                result.cancel()
+                try {
+                    if (!result.isCancelled) {
+                        // If we call cancel(), but the end state is not canceled, it means that
+                        // the result was previously completed successfully. In this case, we need
+                        // to release the underlying reference this Frame is holding on to.
+                        result.getCompleted().asOutput { release(it) }
+                    }
+                } catch (ignored: IllegalStateException) {
+                    // NoOp, this should never happen.
+                }
+            }
+        }
+
+        val status: OutputStatus
+            get() {
+                // A result of `isCancelled` indicates the frame was closed before the Output
+                // arrived.
+                if (count.value == 0 || result.isCancelled) {
+                    return OutputStatus.UNAVAILABLE
+                }
+
+                // If the result is not canceled, and not completed, then we are waiting for the
+                // output to arrive.
+                if (!result.isCompleted) {
+                    return OutputStatus.PENDING
+                }
+
+                // This is guaranteed to be completed.
+                val result = result.getCompleted()
+                if (result is OutputStatus) {
+                    return result
+                }
+                return OutputStatus.AVAILABLE
+            }
+
+        fun getCompletedOrNull(): T? =
+            if (!result.isCompleted || result.isCancelled) {
+                null
+            } else {
+                result.getCompleted().asOutput { acquire(it) }
+            }
+
+        protected fun completeWith(output: T?, outputResult: OutputStatus): Boolean {
+            val result = if (output == null) {
+                result.complete(outputResult)
+            } else {
+                result.complete(output)
+            }
+            return result
+        }
+
+        private inline fun <R> Any.asOutput(block: (T) -> R): R? {
+            if (this is OutputStatus) return null
+            @Suppress("UNCHECKED_CAST")
+            return block(this as T)
+        }
+
+        suspend fun await(): T? = result.await().asOutput { acquire(it) }
+
+        /** Invoked to acquire the underlying resource. */
+        protected abstract fun acquire(value: T): T?
+
+        /** Invoked when the underlying resource is no longer referenced and should be released. */
+        protected abstract fun release(value: T)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+    inner class FrameInfoOutput : FrameOutput<FrameInfo>(),
+        OutputDistributor.OutputListener<FrameInfo> {
+
+        override fun onOutputComplete(
+            cameraFrameNumber: FrameNumber,
+            cameraTimestamp: CameraTimestamp,
+            outputSequence: Long,
+            outputNumber: Long,
+            outputStatus: OutputStatus,
+            output: FrameInfo?
+        ) {
+            check(output == null || output.frameNumber.value == outputNumber) {
+                "Unexpected FrameInfo: $output " +
+                    "Expected ${output?.frameNumber?.value} to match $outputNumber!"
+            }
+
+            completeWith(output, outputStatus)
+            onFrameInfoComplete()
+        }
+
+        override fun acquire(value: FrameInfo): FrameInfo = value
+
+        override fun release(value: FrameInfo) {
+            // Ignored: FrameInfo is not closable.
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+    inner class ImageOutput(val streamId: StreamId) :
+        FrameOutput<SharedOutputImage>(),
+        OutputDistributor.OutputListener<OutputImage> {
+        override fun onOutputComplete(
+            cameraFrameNumber: FrameNumber,
+            cameraTimestamp: CameraTimestamp,
+            outputSequence: Long,
+            outputNumber: Long,
+            outputStatus: OutputStatus,
+            output: OutputImage?
+        ) {
+            check(output == null || output.timestamp == outputNumber) {
+                "Unexpected Image: $output, expected ${output?.timestamp} to match $outputNumber!"
+            }
+            val sharedImage = output?.let { SharedOutputImage.from(it) }
+            if (!completeWith(sharedImage, outputStatus)) {
+                sharedImage?.close()
+            }
+            onStreamResultComplete(streamId)
+        }
+
+        override fun release(value: SharedOutputImage) {
+            value.close()
+        }
+
+        override fun acquire(value: SharedOutputImage): SharedOutputImage? = value.acquireOrNull()
+    }
+}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputDistributor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputDistributor.kt
index 279754d..30a68b1 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputDistributor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputDistributor.kt
@@ -21,6 +21,7 @@
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraTimestamp
 import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.OutputStatus
 import kotlinx.atomicfu.atomic
 
 /**
@@ -51,7 +52,7 @@
     private val outputFinalizer: Finalizer<T>
 ) : AutoCloseable {
 
-    internal interface OutputCompleteListener<T> {
+    internal interface OutputListener<T> {
         /**
          * Invoked when an output is in a completed state, and will *always* be invoked exactly
          * once per [OutputDistributor.onOutputStarted] event.
@@ -65,18 +66,30 @@
             cameraTimestamp: CameraTimestamp,
             outputSequence: Long,
             outputNumber: Long,
-            output: T?
+            outputStatus: OutputStatus,
+            output: T?,
         )
     }
 
     private val lock = Any()
 
-    @GuardedBy("lock") private var closed = false
-    @GuardedBy("lock") private var outputSequenceNumbers = 1L
-    @GuardedBy("lock") private var newestOutputNumber = Long.MIN_VALUE
-    @GuardedBy("lock") private var newestFrameNumber = FrameNumber(Long.MIN_VALUE)
-    @GuardedBy("lock") private var lastFailedFrameNumber = Long.MIN_VALUE
-    @GuardedBy("lock") private var lastFailedOutputNumber = Long.MIN_VALUE
+    @GuardedBy("lock")
+    private var closed = false
+
+    @GuardedBy("lock")
+    private var outputSequenceNumbers = 1L
+
+    @GuardedBy("lock")
+    private var newestOutputNumber = Long.MIN_VALUE
+
+    @GuardedBy("lock")
+    private var newestFrameNumber = FrameNumber(Long.MIN_VALUE)
+
+    @GuardedBy("lock")
+    private var lastFailedFrameNumber = Long.MIN_VALUE
+
+    @GuardedBy("lock")
+    private var lastFailedOutputNumber = Long.MIN_VALUE
 
     private val startedOutputs = mutableListOf<StartedOutput<T>>()
     private val availableOutputs = mutableMapOf<Long, T?>()
@@ -84,7 +97,7 @@
     /**
      * Indicates a camera2 output has started at a particular frameNumber and timestamp as well as
      * supplying the callback to listen for the output to become available. The
-     * [outputCompleteListener] can be invoked synchronously if the output is already available.
+     * [outputListener] can be invoked synchronously if the output is already available.
      *
      * @param cameraFrameNumber The Camera2 FrameNumber for this output
      * @param cameraTimestamp The Camera2 CameraTimestamp for this output
@@ -93,7 +106,7 @@
      *   be the same as the CameraTimestamp, but may also be different if the timebase of the
      *   the images is different), or the value of the frameNumber if this OutputDistributor is
      *   handling metadata.
-     * @param outputCompleteListener will be invoked whenever the output is fully resolved,
+     * @param outputListener will be invoked whenever the output is fully resolved,
      *   either because the output has been successfully matched, or because the output has failed,
      *   or because this OutputDistributor is now closed.
      */
@@ -101,15 +114,17 @@
         cameraFrameNumber: FrameNumber,
         cameraTimestamp: CameraTimestamp,
         outputNumber: Long,
-        outputCompleteListener: OutputCompleteListener<T>
+        outputListener: OutputListener<T>
     ) {
         var outputsToCancel: List<StartedOutput<T>>? = null
         var outputToComplete: T? = null
         var invokeOutputCompleteListener = false
         var outputToFinalize: T? = null
+        val isClosed: Boolean
 
         val outputSequence: Long
         synchronized(lock) {
+            isClosed = closed
             outputSequence = outputSequenceNumbers++
             if (closed ||
                 lastFailedFrameNumber == cameraFrameNumber.value ||
@@ -170,21 +185,38 @@
                     cameraTimestamp,
                     outputSequence,
                     outputNumber,
-                    outputCompleteListener
+                    outputListener
                 )
             )
         }
 
         // Invoke finalizers and listeners outside of the synchronized block to avoid holding locks.
-        outputsToCancel?.forEach { it.completeWith(null) }
+        outputsToCancel?.let {
+            val reason = if (isClosed) {
+                OutputStatus.ERROR_OUTPUT_ABORTED
+            } else {
+                OutputStatus.ERROR_OUTPUT_MISSING
+            }
+            for (output in it) {
+                output.completeWith(null, reason)
+            }
+        }
         outputToFinalize?.let { outputFinalizer.finalize(it) }
         if (invokeOutputCompleteListener) {
-            outputCompleteListener.onOutputComplete(
+            val outputResult = if (isClosed) {
+                OutputStatus.ERROR_OUTPUT_ABORTED
+            } else if (outputToComplete == null) {
+                OutputStatus.ERROR_OUTPUT_DROPPED
+            } else {
+                OutputStatus.AVAILABLE
+            }
+            outputListener.onOutputComplete(
                 cameraFrameNumber = cameraFrameNumber,
                 cameraTimestamp = cameraTimestamp,
                 outputSequence = outputSequence,
                 outputNumber = outputNumber,
-                outputToComplete
+                outputResult,
+                outputToComplete,
             )
         }
     }
@@ -211,7 +243,12 @@
             if (matchingOutput != null) {
                 outputsToCancel = removeOutputsOlderThan(matchingOutput)
 
-                matchingOutput.completeWith(output)
+                // If the output is null, then we know that the output was intentionally dropped.
+                if (output == null) {
+                    matchingOutput.completeWith(null, OutputStatus.ERROR_OUTPUT_DROPPED)
+                } else {
+                    matchingOutput.completeWith(output, OutputStatus.AVAILABLE)
+                }
                 startedOutputs.remove(matchingOutput)
                 return@synchronized
             }
@@ -229,14 +266,14 @@
 
         // Invoke finalizers and listeners outside of the synchronized block to avoid holding locks.
         outputToFinalize?.let { outputFinalizer.finalize(it) }
-        outputsToCancel?.forEach { it.completeWith(null) }
+        outputsToCancel?.forEach { it.completeWith(null, OutputStatus.ERROR_OUTPUT_MISSING) }
     }
 
     /**
      * Indicates an output will not arrive for a specific [FrameNumber].
      */
     fun onOutputFailure(frameNumber: FrameNumber) {
-        var outputToCancel: StartedOutput<T>? = null
+        var outputWithFailure: StartedOutput<T>? = null
 
         synchronized(lock) {
             if (closed) {
@@ -248,12 +285,12 @@
                 ?.let {
                     lastFailedOutputNumber = it.outputNumber
                     startedOutputs.remove(it)
-                    outputToCancel = it
+                    outputWithFailure = it
                 }
         }
 
         // Invoke listeners outside of the synchronized block to avoid holding locks.
-        outputToCancel?.completeWith(null)
+        outputWithFailure?.completeWith(null, OutputStatus.ERROR_OUTPUT_FAILED)
     }
 
     @GuardedBy("lock")
@@ -298,7 +335,7 @@
             outputFinalizer.finalize(pendingOutput)
         }
         for (startedOutput in outputsToCancel) {
-            startedOutput.completeWith(null)
+            startedOutput.completeWith(null, OutputStatus.ERROR_OUTPUT_ABORTED)
         }
     }
 
@@ -312,20 +349,21 @@
         val cameraTimestamp: CameraTimestamp,
         val outputSequence: Long,
         val outputNumber: Long,
-        private val outputCompleteListener: OutputCompleteListener<T>
+        private val outputListener: OutputListener<T>
     ) {
         private val complete = atomic(false)
 
-        fun completeWith(output: T?) {
+        fun completeWith(output: T?, outputResult: OutputStatus) {
             check(complete.compareAndSet(expect = false, update = true)) {
                 "Output $outputSequence at $cameraFrameNumber for $outputNumber was completed " +
                     "multiple times!"
             }
-            outputCompleteListener.onOutputComplete(
+            outputListener.onOutputComplete(
                 cameraFrameNumber,
                 cameraTimestamp,
                 outputSequence,
                 outputNumber,
+                outputResult,
                 output
             )
         }
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/internal/FrameImplTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/internal/FrameImplTest.kt
new file mode 100644
index 0000000..f157e96
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/internal/FrameImplTest.kt
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.internal
+
+import android.os.Build
+import android.util.Size
+import androidx.camera.camera2.pipe.CameraTimestamp
+import androidx.camera.camera2.pipe.Frame.Companion.isFrameInfoAvailable
+import androidx.camera.camera2.pipe.Frame.Companion.isImageAvailable
+import androidx.camera.camera2.pipe.FrameId
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.OutputId
+import androidx.camera.camera2.pipe.OutputStatus
+import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.StreamFormat
+import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.media.OutputImage
+import androidx.camera.camera2.pipe.testing.FakeFrameInfo
+import androidx.camera.camera2.pipe.testing.FakeFrameMetadata
+import androidx.camera.camera2.pipe.testing.FakeImage
+import androidx.camera.camera2.pipe.testing.FakeRequestMetadata
+import androidx.camera.camera2.pipe.testing.FakeSurfaces
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+/** Tests for [FrameImpl] */
+@RunWith(RobolectricTestRunner::class)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class FrameImplTest {
+    private val stream1Id = StreamId(1)
+    private val stream2Id = StreamId(2)
+
+    private val output1Id = OutputId(10)
+    private val output2Id = OutputId(12)
+    private val output3Id = OutputId(13)
+
+    private val fakeSurfaces = FakeSurfaces()
+    private val stream1Surface = fakeSurfaces.createFakeSurface(Size(640, 480))
+    private val stream2Surface = fakeSurfaces.createFakeSurface(Size(640, 480))
+    private val streamToSurfaceMap = mapOf(stream1Id to stream1Surface, stream2Id to stream2Surface)
+
+    private val frameId = FrameId(240)
+    private val frameNumber = FrameNumber(420)
+    private val frameTimestampNs = 1234L
+    private val frameTimestamp = CameraTimestamp(frameTimestampNs)
+
+    private val imageStreams = setOf(stream1Id, stream2Id)
+    private val request = Request(streams = listOf(stream1Id, stream2Id))
+    private val fakeRequestMetadata = FakeRequestMetadata.from(
+        request,
+        streamToSurfaceMap,
+        repeating = false
+    )
+
+    private val frameState = FrameState(
+        requestMetadata = fakeRequestMetadata,
+        frameId = frameId,
+        frameNumber = frameNumber,
+        frameTimestamp = frameTimestamp,
+        imageStreams
+    )
+
+    private val frameInfoResult = frameState.frameInfoOutput
+    private val streamResult1 = frameState.imageOutputs.first { it.streamId == stream1Id }
+    private val streamResult2 = frameState.imageOutputs.first { it.streamId == stream2Id }
+
+    private val stream1Image = FakeImage(640, 480, StreamFormat.YUV_420_888.value, frameTimestampNs)
+    private val stream2Image = FakeImage(640, 480, StreamFormat.YUV_420_888.value, frameTimestampNs)
+
+    private val stream1OutputImage = OutputImage.from(stream1Id, output1Id, stream1Image)
+    private val stream2OutputImage = OutputImage.from(stream2Id, output3Id, stream2Image)
+    private val fakeFrameMetadata = FakeFrameMetadata(frameNumber = frameNumber)
+    private val fakeFrameInfo = FakeFrameInfo(metadata = fakeFrameMetadata)
+
+    private val sharedOutputFrame = FrameImpl(frameState)
+
+    @Test
+    fun sharedOutputFrameHasResults() {
+        assertThat(sharedOutputFrame.frameNumber).isEqualTo(frameNumber)
+        assertThat(sharedOutputFrame.frameTimestamp).isEqualTo(frameTimestamp)
+        assertThat(sharedOutputFrame.imageStreams).containsExactly(stream1Id, stream2Id)
+    }
+
+    @Test
+    fun closingSharedOutputFrameCompletesImageResults() {
+        sharedOutputFrame.close()
+
+        assertThat(sharedOutputFrame.frameInfoStatus).isEqualTo(OutputStatus.UNAVAILABLE)
+
+        assertThat(streamResult1.status).isEqualTo(OutputStatus.UNAVAILABLE)
+        assertThat(streamResult2.status).isEqualTo(OutputStatus.UNAVAILABLE)
+        assertThat(frameInfoResult.status).isEqualTo(OutputStatus.UNAVAILABLE)
+    }
+
+    @Test
+    fun completedOutputsAreAvailableFromFrame() {
+        distributeAllOutputs()
+
+        assertThat(sharedOutputFrame.isFrameInfoAvailable).isTrue()
+        assertThat(sharedOutputFrame.getFrameInfo()).isSameInstanceAs(fakeFrameInfo)
+
+        assertThat(sharedOutputFrame.isImageAvailable(stream1Id)).isTrue()
+        val outputImage1 = sharedOutputFrame.getImage(stream1Id)!!
+        assertThat(outputImage1.streamId).isEqualTo(stream1Id)
+        assertThat(outputImage1.outputId).isEqualTo(output1Id)
+
+        assertThat(sharedOutputFrame.isImageAvailable(stream2Id)).isTrue()
+        val outputImage2 = sharedOutputFrame.getImage(stream2Id)!!
+        assertThat(outputImage2.streamId).isEqualTo(stream2Id)
+        assertThat(outputImage2.outputId).isEqualTo(output3Id)
+
+        assertThat(stream1Image.isClosed).isFalse()
+        assertThat(stream2Image.isClosed).isFalse()
+
+        sharedOutputFrame.close()
+
+        assertThat(stream1Image.isClosed).isFalse()
+        assertThat(stream2Image.isClosed).isFalse()
+
+        outputImage1.close()
+        outputImage2.close()
+
+        assertThat(stream1Image.isClosed).isTrue()
+        assertThat(stream2Image.isClosed).isTrue()
+    }
+
+    @Test
+    fun outputsCompletedAfterSharedOutputFrameIsClosedAreAlsoClosed() {
+        sharedOutputFrame.close()
+        distributeAllOutputs()
+
+        assertThat(stream1Image.isClosed).isTrue()
+        assertThat(stream2Image.isClosed).isTrue()
+    }
+
+    @Test
+    fun completedOutputsAreClosedAfterFrameIsClosed() {
+        distributeAllOutputs()
+        sharedOutputFrame.close()
+
+        assertThat(stream1Image.isClosed).isTrue()
+        assertThat(stream2Image.isClosed).isTrue()
+    }
+
+    @Test
+    fun outputsAcquiredBeforeClosedAreNotClosedImmediately() {
+        distributeAllOutputs()
+
+        val output11 = sharedOutputFrame.getImage(stream1Id)!!
+        val output12 = sharedOutputFrame.getImage(stream1Id)!!
+        val output13 = sharedOutputFrame.getImage(stream1Id)!!
+
+        val output21 = sharedOutputFrame.getImage(stream2Id)!!
+
+        sharedOutputFrame.close()
+
+        assertThat(stream1Image.isClosed).isFalse()
+        assertThat(stream2Image.isClosed).isFalse()
+
+        // Once we close the specific output, the underlying image should be closed:
+        output21.close()
+        assertThat(stream1Image.isClosed).isFalse()
+        assertThat(stream2Image.isClosed).isTrue()
+
+        // Close some, but not all images:
+        output11.close()
+        output12.close()
+        assertThat(stream1Image.isClosed).isFalse()
+        assertThat(stream2Image.isClosed).isTrue()
+
+        // Close final image
+        output13.close()
+        assertThat(stream1Image.isClosed).isTrue()
+        assertThat(stream2Image.isClosed).isTrue()
+    }
+
+    @Test
+    fun sharedOutputFramesCanBeForkedBeforeCompleted() {
+        val frame2 = sharedOutputFrame.tryAcquire()!!
+        sharedOutputFrame.close()
+
+        // Distribute outputs after the initial frame is closed.
+        distributeAllOutputs()
+
+        // Assert outputs are not closed, and that outputs are still available.
+        assertThat(frame2.isFrameInfoAvailable).isTrue()
+        assertThat(frame2.getFrameInfo()).isSameInstanceAs(fakeFrameInfo)
+
+        assertThat(frame2.isImageAvailable(stream1Id)).isTrue()
+        val stream1OutputImage = frame2.getImage(stream1Id)!!
+        assertThat(stream1OutputImage.streamId).isEqualTo(stream1Id)
+        assertThat(stream1OutputImage.outputId).isEqualTo(output1Id)
+
+        assertThat(frame2.isImageAvailable(stream2Id)).isTrue()
+        val stream2OutputImage = frame2.getImage(stream2Id)!!
+        assertThat(stream2OutputImage.streamId).isEqualTo(stream2Id)
+        assertThat(stream2OutputImage.outputId).isEqualTo(output3Id)
+
+        assertThat(stream1Image.isClosed).isFalse()
+        assertThat(stream2Image.isClosed).isFalse()
+    }
+
+    @Test
+    fun closingForkedFramesAndOutputsClosesAllOutputs() {
+        val frame2 = sharedOutputFrame.tryAcquire()!!
+        val frame3 = sharedOutputFrame.tryAcquire()!!
+        distributeAllOutputs()
+
+        // Acquire a few outputs from various frames.
+        val f1Output = sharedOutputFrame.getImage(stream1Id)!!
+        val f2Output = frame2.getImage(stream1Id)!!
+        val f3Output = frame3.getImage(stream1Id)!!
+
+        // Close all the frames
+        sharedOutputFrame.close()
+        frame2.close()
+        frame3.close()
+
+        // Stream1 image is not closed (3 outstanding image references)
+        assertThat(stream1Image.isClosed).isFalse()
+        // Stream2 image is now closed (0 outstanding image references, 0 frame references)
+        assertThat(stream2Image.isClosed).isTrue()
+
+        // Close 2/3 of the outputs
+        f1Output.close()
+        f3Output.close()
+        assertThat(stream1Image.isClosed).isFalse()
+
+        // Close last output
+        f2Output.close()
+        assertThat(stream2Image.isClosed).isTrue()
+    }
+
+    @Test
+    fun sharedFramesCanBeForkedWithASubsetOfOutputs() {
+        val frame2 = sharedOutputFrame.tryAcquire(setOf(stream1Id))!!
+        val frame3 = sharedOutputFrame.tryAcquire(setOf(stream2Id))!!
+        val frame4 = sharedOutputFrame.tryAcquire(setOf(StreamId(42)))!! // Unsupported Stream
+
+        distributeAllOutputs()
+
+        assertThat(frame2.imageStreams).containsExactly(stream1Id)
+        assertThat(frame3.imageStreams).containsExactly(stream2Id)
+        assertThat(frame4.imageStreams).isEmpty()
+
+        assertThat(frame2.isImageAvailable(stream1Id)).isTrue()
+        assertThat(frame3.isImageAvailable(stream2Id)).isTrue()
+
+        sharedOutputFrame.close()
+        assertThat(stream1Image.isClosed).isFalse()
+        assertThat(stream2Image.isClosed).isFalse()
+
+        frame3.close()
+        assertThat(stream1Image.isClosed).isFalse()
+        assertThat(stream2Image.isClosed).isTrue()
+
+        frame2.close()
+        assertThat(stream1Image.isClosed).isTrue()
+        assertThat(stream2Image.isClosed).isTrue()
+    }
+
+    @Test
+    fun accessingFrameAfterCloseReturnsNull() {
+        sharedOutputFrame.close()
+
+        assertThat(sharedOutputFrame.getImage(stream1Id)).isNull()
+        assertThat(sharedOutputFrame.getImage(stream2Id)).isNull()
+        assertThat(sharedOutputFrame.getFrameInfo()).isNull()
+    }
+
+    @Test
+    fun forkingAClosedFrameReturnsNull() {
+        sharedOutputFrame.close()
+
+        val frame = sharedOutputFrame.tryAcquire()
+        assertThat(frame).isNull()
+    }
+
+    @Test
+    fun forkCanOnlyAccessKnownStreams() {
+        val stream1Frame = sharedOutputFrame.tryAcquire(setOf(stream1Id))!!
+        val stream2Frame = stream1Frame.tryAcquire(setOf(stream2Id))!!
+        val allStreamFrame = stream1Frame.tryAcquire(setOf(stream1Id, stream2Id))!!
+
+        assertThat(stream2Frame.imageStreams).isEmpty()
+        assertThat(allStreamFrame.imageStreams).containsExactly(stream1Id)
+    }
+
+    @Test
+    fun frameReportsFailureReason() {
+        streamResult1.onOutputComplete(
+            frameNumber,
+            frameTimestamp,
+            42,
+            frameTimestamp.value,
+            OutputStatus.ERROR_OUTPUT_DROPPED,
+            null
+        )
+
+        assertThat(sharedOutputFrame.imageStatus(stream1Id))
+            .isEqualTo(OutputStatus.ERROR_OUTPUT_DROPPED)
+        assertThat(sharedOutputFrame.getImage(stream1Id)).isNull()
+
+        sharedOutputFrame.close()
+
+        assertThat(sharedOutputFrame.imageStatus(stream1Id))
+            .isEqualTo(OutputStatus.UNAVAILABLE)
+        assertThat(sharedOutputFrame.getImage(stream1Id)).isNull()
+    }
+
+    @After
+    fun cleanup() {
+        fakeSurfaces.close()
+    }
+
+    private fun distributeAllOutputs() {
+        // Complete streamResult1 with stream1Output1Image
+        streamResult1.onOutputComplete(
+            frameNumber,
+            frameTimestamp,
+            42,
+            frameTimestamp.value,
+            OutputStatus.AVAILABLE,
+            stream1OutputImage
+        )
+
+        // Complete streamResult2 with stream2Output3Image
+        streamResult2.onOutputComplete(
+            frameNumber,
+            frameTimestamp,
+            42,
+            frameTimestamp.value,
+            OutputStatus.AVAILABLE,
+            stream2OutputImage
+        )
+
+        // Complete frameInfoResult
+        frameInfoResult.onOutputComplete(
+            frameNumber,
+            frameTimestamp,
+            42,
+            frameNumber.value,
+            OutputStatus.AVAILABLE,
+            fakeFrameInfo
+        )
+    }
+}
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/internal/FrameStateTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/internal/FrameStateTest.kt
new file mode 100644
index 0000000..6d28f9e
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/internal/FrameStateTest.kt
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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 android.os.Build
+import androidx.camera.camera2.pipe.CameraTimestamp
+import androidx.camera.camera2.pipe.FrameId
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.OutputId
+import androidx.camera.camera2.pipe.OutputStatus
+import androidx.camera.camera2.pipe.StreamFormat
+import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.media.OutputImage
+import androidx.camera.camera2.pipe.testing.FakeFrameInfo
+import androidx.camera.camera2.pipe.testing.FakeFrameMetadata
+import androidx.camera.camera2.pipe.testing.FakeImage
+import androidx.camera.camera2.pipe.testing.FakeRequestMetadata
+import androidx.camera.camera2.pipe.testing.FakeSurfaces
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+@RunWith(RobolectricTestRunner::class)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class FrameStateTest {
+
+    private val stream1Id = StreamId(1)
+    private val stream2Id = StreamId(2)
+
+    private val output1Id = OutputId(1)
+
+    private val frameId = FrameId(240)
+    private val frameNumber = FrameNumber(420)
+    private val frameTimestampNs = 1234L
+    private val frameTimestamp = CameraTimestamp(frameTimestampNs)
+
+    private val imageStreams = setOf(stream1Id, stream2Id)
+    private val fakeImage = FakeImage(640, 480, StreamFormat.YUV_420_888.value, frameTimestampNs)
+    private val outputImage = OutputImage.from(
+        stream1Id,
+        output1Id,
+        fakeImage
+    )
+    private val fakeSurfaces = FakeSurfaces()
+    private val stream1Surface = fakeSurfaces.createFakeSurface()
+    private val stream2Surface = fakeSurfaces.createFakeSurface()
+
+    private val fakeRequestMetadata = FakeRequestMetadata(
+        streams = mapOf(stream1Id to stream1Surface, stream2Id to stream2Surface)
+    )
+    private val fakeFrameMetadata = FakeFrameMetadata(
+        frameNumber = frameNumber
+    )
+    private val fakeFrameInfo = FakeFrameInfo(
+        metadata = fakeFrameMetadata,
+        requestMetadata = fakeRequestMetadata
+    )
+
+    private val frameState = FrameState(
+        requestMetadata = fakeRequestMetadata,
+        frameId = frameId,
+        frameNumber = frameNumber,
+        frameTimestamp = frameTimestamp,
+        imageStreams
+    )
+
+    private val imageResult1 = frameState.imageOutputs.first { it.streamId == stream1Id }
+    private val imageResult2 = frameState.imageOutputs.first { it.streamId == stream2Id }
+
+    @After
+    fun cleanup() {
+        fakeSurfaces.close()
+    }
+
+    @Test
+    fun streamResultsAreNotStartedByDefault() {
+        assertThat(imageResult1.status).isEqualTo(OutputStatus.PENDING)
+    }
+
+    @Test
+    fun streamResultKeepsCountBeforeCancelling() {
+        imageResult1.increment() // 2
+        imageResult1.increment() // 3
+        imageResult1.decrement() // 2
+        imageResult1.increment() // 3
+        imageResult1.decrement() // 2
+        imageResult1.decrement() // 1
+
+        assertThat(imageResult1.status).isEqualTo(OutputStatus.PENDING)
+
+        imageResult1.decrement() // 0 -> Close/Cancel
+
+        assertThat(imageResult1.status).isEqualTo(OutputStatus.UNAVAILABLE)
+    }
+
+    @Test
+    fun streamResultClosesOutputAfterCountReachesZero() {
+        imageResult1.onOutputComplete(
+            frameNumber,
+            frameTimestamp,
+            64L,
+            frameTimestampNs,
+            OutputStatus.AVAILABLE,
+            outputImage
+        )
+
+        assertThat(fakeImage.isClosed).isFalse()
+        assertThat(imageResult1.status).isEqualTo(OutputStatus.AVAILABLE)
+
+        imageResult1.decrement() // 0 -> Close/Cancel
+
+        assertThat(fakeImage.isClosed).isTrue()
+        assertThat(imageResult1.status).isEqualTo(OutputStatus.UNAVAILABLE)
+
+        val result = imageResult1.getCompletedOrNull()
+        assertThat(result).isNull()
+    }
+
+    @Test
+    fun streamResultAfterCountReachesZeroIsClosed() {
+        imageResult1.decrement() // 0 -> Close/Cancel
+
+        imageResult1.onOutputComplete(
+            frameNumber,
+            frameTimestamp,
+            64L,
+            frameTimestampNs,
+            OutputStatus.AVAILABLE,
+            outputImage
+        )
+
+        assertThat(fakeImage.isClosed).isTrue()
+        assertThat(imageResult1.getCompletedOrNull()).isNull()
+    }
+
+    @Test
+    fun acquiringAnImageAndThenClosingResultDoesNotCloseImage() {
+        imageResult1.onOutputComplete(
+            frameNumber,
+            frameTimestamp,
+            64L,
+            frameTimestampNs,
+            OutputStatus.AVAILABLE,
+            outputImage
+        )
+        val imageCopy1 = imageResult1.getCompletedOrNull()
+        val imageCopy2 = imageResult1.getCompletedOrNull()
+
+        assertThat(imageCopy1).isNotNull()
+        assertThat(imageCopy2).isNotNull()
+        assertThat(fakeImage.isClosed).isFalse()
+
+        imageResult1.decrement() // 0 -> Close/Cancel
+
+        assertThat(fakeImage.isClosed).isFalse()
+
+        imageCopy2!!.close()
+        assertThat(fakeImage.isClosed).isFalse()
+
+        imageCopy1!!.close() // All references are released, closing the underlying image.
+        assertThat(fakeImage.isClosed).isTrue()
+    }
+
+    @Test
+    fun frameInfoResultCanBeCompleted() {
+        frameState.frameInfoOutput.onOutputComplete(
+            frameNumber,
+            frameTimestamp,
+            10,
+            frameNumber.value,
+            OutputStatus.AVAILABLE,
+            fakeFrameInfo
+        )
+
+        assertThat(frameState.frameInfoOutput.status).isEqualTo(OutputStatus.AVAILABLE)
+        assertThat(frameState.frameInfoOutput.getCompletedOrNull()).isSameInstanceAs(fakeFrameInfo)
+    }
+
+    @Test
+    fun frameInfoResultAfterCanceledIsNull() {
+        frameState.frameInfoOutput.decrement()
+        frameState.frameInfoOutput.onOutputComplete(
+            frameNumber,
+            frameTimestamp,
+            10,
+            frameNumber.value,
+            OutputStatus.AVAILABLE,
+            fakeFrameInfo
+        )
+
+        assertThat(frameState.frameInfoOutput.status).isEqualTo(OutputStatus.UNAVAILABLE)
+        assertThat(frameState.frameInfoOutput.getCompletedOrNull()).isNull()
+    }
+}
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/media/OutputDistributorTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/media/OutputDistributorTest.kt
index 25f3235..af11146 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/media/OutputDistributorTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/media/OutputDistributorTest.kt
@@ -19,7 +19,8 @@
 import android.os.Build
 import androidx.camera.camera2.pipe.CameraTimestamp
 import androidx.camera.camera2.pipe.FrameNumber
-import androidx.camera.camera2.pipe.media.OutputDistributor.OutputCompleteListener
+import androidx.camera.camera2.pipe.OutputStatus
+import androidx.camera.camera2.pipe.media.OutputDistributor.OutputListener
 import com.google.common.truth.Truth.assertThat
 import kotlinx.atomicfu.atomic
 import org.junit.Test
@@ -149,6 +150,7 @@
 
         assertThat(pendingOutput1.isComplete).isTrue()
         assertThat(pendingOutput1.output).isEqualTo(fakeOutput1)
+        assertThat(pendingOutput1.outputStatus).isEqualTo(OutputStatus.AVAILABLE)
     }
 
     @Test
@@ -167,6 +169,7 @@
 
         assertThat(pendingOutput1.isComplete).isTrue()
         assertThat(pendingOutput1.output).isNull()
+        assertThat(pendingOutput1.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_DROPPED)
     }
 
     @Test
@@ -187,6 +190,9 @@
         assertThat(pendingOutput2.output).isNull() // #2 is Canceled
         assertThat(pendingOutput3.output).isEqualTo(fakeOutput3)
         assertThat(pendingOutput4.output).isNull() // #4 is still pending
+
+        assertThat(pendingOutput1.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_MISSING)
+        assertThat(pendingOutput2.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_MISSING)
     }
 
     @Test
@@ -200,6 +206,8 @@
 
         assertThat(pendingOutput1.isComplete).isTrue()
         assertThat(pendingOutput2.isComplete).isTrue()
+        assertThat(pendingOutput1.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_ABORTED)
+        assertThat(pendingOutput2.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_ABORTED)
         assertThat(pendingOutput1.output).isNull()
         assertThat(pendingOutput2.output).isNull()
     }
@@ -215,6 +223,8 @@
 
         assertThat(pendingOutput1.isComplete).isTrue()
         assertThat(pendingOutput2.isComplete).isTrue()
+        assertThat(pendingOutput1.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_ABORTED)
+        assertThat(pendingOutput2.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_ABORTED)
         assertThat(pendingOutput1.output).isNull()
         assertThat(pendingOutput2.output).isNull()
     }
@@ -232,9 +242,13 @@
 
         assertThat(pendingOutput1.isComplete).isTrue()
         assertThat(pendingOutput2.isComplete).isTrue()
+
         assertThat(pendingOutput1.output).isNull()
         assertThat(pendingOutput2.output).isNull()
 
+        assertThat(pendingOutput1.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_ABORTED)
+        assertThat(pendingOutput2.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_ABORTED)
+
         assertThat(fakeOutput1.finalized).isTrue()
         assertThat(fakeOutput2.finalized).isTrue()
     }
@@ -252,9 +266,13 @@
 
         assertThat(pendingOutput1.isComplete).isTrue()
         assertThat(pendingOutput2.isComplete).isTrue()
+
         assertThat(pendingOutput1.output).isNull()
         assertThat(pendingOutput2.output).isNull()
 
+        assertThat(pendingOutput1.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_ABORTED)
+        assertThat(pendingOutput2.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_ABORTED)
+
         assertThat(fakeOutput1.finalized).isTrue()
         assertThat(fakeOutput2.finalized).isTrue()
     }
@@ -299,6 +317,10 @@
         assertThat(pendingOutput1.output).isNull()
         assertThat(pendingOutput2.output).isNull()
         assertThat(pendingOutput3.output).isEqualTo(fakeOutput3)
+
+        assertThat(pendingOutput1.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_MISSING)
+        assertThat(pendingOutput2.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_MISSING)
+        assertThat(pendingOutput3.outputStatus).isEqualTo(OutputStatus.AVAILABLE)
     }
 
     @Test
@@ -347,6 +369,7 @@
         assertThat(pendingOutput3.isComplete).isFalse()
 
         assertThat(pendingOutput2.output).isNull()
+        assertThat(pendingOutput2.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_FAILED)
     }
 
     @Test
@@ -366,6 +389,10 @@
         assertThat(pendingOutput1.output).isNull()
         assertThat(pendingOutput2.output).isNull()
         assertThat(pendingOutput3.output).isNull()
+
+        assertThat(pendingOutput1.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_FAILED)
+        assertThat(pendingOutput2.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_FAILED)
+        assertThat(pendingOutput3.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_FAILED)
     }
 
     @Test
@@ -381,6 +408,7 @@
         assertThat(pendingOutput3.isComplete).isFalse()
 
         assertThat(pendingOutput2.output).isNull()
+        assertThat(pendingOutput2.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_DROPPED)
     }
 
     @Test
@@ -397,29 +425,32 @@
         assertThat(fakeOutput1.finalized).isTrue()
         assertThat(pendingOutput1.isComplete).isTrue()
         assertThat(pendingOutput1.output).isNull()
+        assertThat(pendingOutput1.outputStatus).isEqualTo(OutputStatus.ERROR_OUTPUT_DROPPED)
     }
 
     /**
-     * Utility class that implements [OutputCompleteListener] and can be used to observe when an
+     * Utility class that implements [OutputListener] and can be used to observe when an
      * output is complete and the callback is invoked.
      */
     private class PendingOutput(
         val cameraFrameNumber: FrameNumber,
         val cameraTimestamp: CameraTimestamp,
         val outputNumber: Long
-    ) : OutputCompleteListener<FakeOutput> {
+    ) : OutputListener<FakeOutput> {
         private val _complete = atomic(false)
         val isComplete: Boolean
             get() = _complete.value
 
         var outputSequence: Long? = null
         var output: FakeOutput? = null
+        var outputStatus: OutputStatus? = null
 
         override fun onOutputComplete(
             cameraFrameNumber: FrameNumber,
             cameraTimestamp: CameraTimestamp,
             outputSequence: Long,
             outputNumber: Long,
+            outputStatus: OutputStatus,
             output: FakeOutput?
         ) {
             // Assert that this callback has only been invoked once.
@@ -432,6 +463,7 @@
 
             // Record the actual output and outputSequence for future checks.
             this.outputSequence = outputSequence
+            this.outputStatus = outputStatus
             this.output = output
         }
     }
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 44b0f84..ae3db88 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -46,7 +46,7 @@
     testImplementation(libs.robolectric)
     testImplementation(libs.mockitoCore4)
     testImplementation(libs.kotlinCoroutinesTest)
-    testImplementation(project(":annotation:annotation-experimental"))
+    testImplementation("androidx.annotation:annotation-experimental:1.4.0")
     testImplementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
     testImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.3.1")
     testImplementation(project(":camera:camera-video"))
@@ -71,7 +71,7 @@
     }
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.kotlinCoroutinesAndroid)
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
     androidTestImplementation(project(":internal-testutils-truth"))
     androidTestImplementation("org.jetbrains.kotlinx:atomicfu:0.13.1")
     androidTestImplementation("androidx.exifinterface:exifinterface:1.0.0")
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.kt
new file mode 100644
index 0000000..b9e38c1
--- /dev/null
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.internal
+
+import android.content.Context
+import android.hardware.camera2.CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.internal.compat.CameraManagerCompat
+import androidx.camera.core.CameraSelector
+import androidx.camera.testing.impl.CameraUtil
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Contains tests for [Camera2CameraInfoImpl] internal implementation.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 21)
+class Camera2CameraInfoImplTest {
+
+    @get:Rule
+    val cameraRule = CameraUtil.grantCameraPermissionAndPreTest(
+        CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
+    )
+
+    private val lensFacing = CameraSelector.LENS_FACING_BACK
+    private lateinit var camera2CameraInfo: Camera2CameraInfoImpl
+
+    @Before
+    fun setUp() {
+        val cameraId = CameraUtil.getCameraIdWithLensFacing(lensFacing)!!
+        val cameraManagerCompat = CameraManagerCompat.from((getApplicationContext() as Context))
+        camera2CameraInfo = Camera2CameraInfoImpl(cameraId, cameraManagerCompat)
+    }
+
+    @Test
+    fun canReturnSupportedOutputFormats() {
+        val formats = camera2CameraInfo.supportedOutputFormats.toList()
+        val cameraCharacteristics = CameraUtil.getCameraCharacteristics(lensFacing)!!
+        val streamConfigurationMap = cameraCharacteristics.get(SCALER_STREAM_CONFIGURATION_MAP)!!
+
+        assertThat(formats).containsExactlyElementsIn(streamConfigurationMap.outputFormats.toList())
+    }
+}
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProviderTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProviderTest.kt
index 4c6240b..6e93c64 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProviderTest.kt
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProviderTest.kt
@@ -27,6 +27,7 @@
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_8
 import androidx.camera.testing.impl.CameraUtil
+import androidx.camera.testing.impl.LabTestRule
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -70,6 +71,9 @@
     @get:Rule
     val useCamera = CameraUtil.grantCameraPermissionAndPreTest()
 
+    @get:Rule
+    val labTestRule = LabTestRule()
+
     @Before
     fun setup() {
         assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK))
@@ -124,6 +128,7 @@
 
         val profiles = CamcorderProfile.getAll(cameraId, quality)
         val video = profiles!!.videoProfiles[0]
+        assumeTrue(video != null)
         val audio = profiles.audioProfiles[0]
         val profilesProxy = encoderProfilesProvider.getAll(quality)
         val videoProxy = profilesProxy!!.videoProfiles[0]
@@ -153,10 +158,10 @@
     @Test
     fun afterApi33_hasSameContentAsEncoderProfiles() {
         assumeTrue(encoderProfilesProvider.hasProfile(quality))
-        skipTestOnDevicesWithProblematicBuild()
 
         val profiles = CamcorderProfile.getAll(cameraId, quality)
         val video = profiles!!.videoProfiles[0]
+        assumeTrue(video != null)
         val audio = profiles.audioProfiles[0]
         val profilesProxy = encoderProfilesProvider.getAll(quality)
         val videoProxy = profilesProxy!!.videoProfiles[0]
@@ -182,6 +187,16 @@
         assertThat(audioProxy.profile).isEqualTo(audio.profile)
     }
 
+    @LabTestRule.LabTestOnly
+    @SdkSuppress(minSdkVersion = 31)
+    @Test
+    fun detectNullVideoProfile() {
+        assumeTrue(CamcorderProfile.hasProfile(intCameraId, quality))
+        skipTestOnDevicesWithProblematicBuild()
+        val profiles = CamcorderProfile.getAll(cameraId, quality)!!
+        assertThat(profiles.videoProfiles[0]).isNotNull()
+    }
+
     private fun skipTestOnDevicesWithProblematicBuild() {
         // Skip test for b/265613005, b/223439995 and b/277174217
         val hasVideoProfilesQuirk = DeviceQuirks.get(InvalidVideoProfilesQuirk::class.java) != null
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatTest.kt
new file mode 100644
index 0000000..3acdbee
--- /dev/null
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.internal.compat
+
+import android.hardware.camera2.CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
+import android.hardware.camera2.params.StreamConfigurationMap
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.internal.compat.StreamConfigurationMapCompat.toStreamConfigurationMapCompat
+import androidx.camera.camera2.internal.compat.workaround.OutputSizesCorrector
+import androidx.camera.core.CameraSelector
+import androidx.camera.testing.impl.CameraUtil
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Contains tests for [StreamConfigurationMapCompat].
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 21)
+class StreamConfigurationMapCompatTest {
+
+    @get:Rule
+    val cameraRule = CameraUtil.grantCameraPermissionAndPreTest(
+        CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
+    )
+
+    private val lensFacing = CameraSelector.LENS_FACING_BACK
+    private lateinit var streamConfigurationMap: StreamConfigurationMap
+    private lateinit var streamConfigurationMapCompat: StreamConfigurationMapCompat
+
+    @Before
+    fun setUp() {
+        val cameraCharacteristics = CameraUtil.getCameraCharacteristics(lensFacing)!!
+        streamConfigurationMap = cameraCharacteristics.get(SCALER_STREAM_CONFIGURATION_MAP)!!
+        val cameraId = CameraUtil.getCameraIdWithLensFacing(lensFacing)!!
+        streamConfigurationMapCompat = toStreamConfigurationMapCompat(
+            streamConfigurationMap,
+            OutputSizesCorrector(cameraId)
+        )
+    }
+
+    @Test
+    fun canGetOutputFormats() {
+        val formats = streamConfigurationMapCompat.outputFormats!!.toList()
+        assertThat(formats).containsExactlyElementsIn(streamConfigurationMap.outputFormats.toList())
+    }
+}
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 f76aac6..6d83e9f 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
@@ -429,6 +429,23 @@
 
     @NonNull
     @Override
+    public Set<Integer> getSupportedOutputFormats() {
+        StreamConfigurationMapCompat mapCompat =
+                mCameraCharacteristicsCompat.getStreamConfigurationMapCompat();
+        int[] formats = mapCompat.getOutputFormats();
+        if (formats == null) {
+            return new HashSet<>();
+        }
+
+        Set<Integer> result = new HashSet<>();
+        for (int format : formats) {
+            result.add(format);
+        }
+        return result;
+    }
+
+    @NonNull
+    @Override
     public List<Size> getSupportedResolutions(int format) {
         StreamConfigurationMapCompat mapCompat =
                 mCameraCharacteristicsCompat.getStreamConfigurationMapCompat();
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompat.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompat.java
index 3dddea7..45edcf1 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompat.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompat.java
@@ -71,6 +71,23 @@
         return new StreamConfigurationMapCompat(map, outputSizesCorrector);
     }
 
+
+    /**
+     * Get the image format output formats in this stream configuration.
+     *
+     * <p>All image formats returned by this function will be defined in either ImageFormat or in
+     * PixelFormat.
+     *
+     * @return an array of integer format
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    @Nullable
+    public int[] getOutputFormats() {
+        int[] result = mImpl.getOutputFormats();
+        return result == null ? null : result.clone();
+    }
+
     /**
      * Get a list of sizes compatible with the requested image {@code format}.
      *
@@ -169,6 +186,9 @@
     interface StreamConfigurationMapCompatImpl {
 
         @Nullable
+        int[] getOutputFormats();
+
+        @Nullable
         Size[] getOutputSizes(int format);
 
         @Nullable
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java
index 74854bb..0e7ec89 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java
@@ -39,6 +39,12 @@
 
     @Nullable
     @Override
+    public int[] getOutputFormats() {
+        return mStreamConfigurationMap.getOutputFormats();
+    }
+
+    @Nullable
+    @Override
     public Size[] getOutputSizes(int format) {
         Size[] sizes;
         if (format == ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE) {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java
index 842e16c..176fb41 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java
@@ -30,14 +30,14 @@
  * Quirk denoting the video profile list returns by {@link EncoderProfiles} is invalid.
  *
  * <p>QuirkSummary
- *     Bug Id: 267727595, 278860860, 298951126, 298952500
+ *     Bug Id: 267727595, 278860860, 298951126, 298952500, 320747756
  *     Description: When using {@link EncoderProfiles} on some builds of Android API 33,
  *                  {@link EncoderProfiles#getVideoProfiles()} returns a list with size one, but
  *                  the single value in the list is null. This is not the expected behavior, and
  *                  makes {@link EncoderProfiles} lack of video information.
  *     Device(s): Pixel 4 and above pixel devices with TP1A or TD1A builds (API 33), Samsung devices
- *                 with TP1A build (API 33), Xiaomi devices with TKQ1 build (API 33), OnePlus and
- *                 Oppo devices with API 33 build.
+ *                 with TP1A build (API 33), Xiaomi devices with TKQ1/TP1A build (API 33), OnePlus
+ *                 and Oppo devices with API 33 build.
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class InvalidVideoProfilesQuirk implements Quirk {
@@ -63,7 +63,8 @@
 
     private static final List<String> AFFECTED_OPPO_MODELS = Arrays.asList(
             "cph2437",
-            "cph2525"
+            "cph2525",
+            "pht110"
     );
 
     static boolean load() {
@@ -89,7 +90,7 @@
 
     private static boolean isAffectedXiaomiDevices() {
         return ("redmi".equalsIgnoreCase(Build.BRAND) || "xiaomi".equalsIgnoreCase(Build.BRAND))
-                && isTkq1Build();
+                && (isTkq1Build() || isTp1aBuild());
     }
 
     private static boolean isAffectedPixelModel() {
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index bc1c4d8..520f97c 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -33,7 +33,7 @@
     api("androidx.annotation:annotation:1.2.0")
     api("androidx.lifecycle:lifecycle-livedata:2.1.0")
     api(libs.guavaListenableFuture)
-    api(project(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
     api(libs.kotlinStdlib) // Added for annotation-experimental
     api("androidx.core:core:1.1.0")
     implementation("androidx.exifinterface:exifinterface:1.3.2")
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/Image2JpegBytesDeviceTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/Image2JpegBytesDeviceTest.kt
index bbf5018..ba68c10 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/Image2JpegBytesDeviceTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/Image2JpegBytesDeviceTest.kt
@@ -26,6 +26,7 @@
 import androidx.camera.core.imagecapture.Utils.ROTATION_DEGREES
 import androidx.camera.core.imagecapture.Utils.WIDTH
 import androidx.camera.core.impl.utils.Exif.createFromInputStream
+import androidx.camera.core.internal.compat.quirk.DeviceQuirks
 import androidx.camera.core.processing.Packet
 import androidx.camera.testing.impl.TestImageUtil.createYuvFakeImageProxy
 import androidx.camera.testing.impl.fakes.FakeImageInfo
@@ -45,7 +46,7 @@
 @SdkSuppress(minSdkVersion = 21)
 class Image2JpegBytesDeviceTest {
 
-    private val operation = Image2JpegBytes()
+    private val operation = Image2JpegBytes(DeviceQuirks.getAll())
 
     @Test
     fun processYuvImage_assertOutput() {
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 a1f2142..641fda4 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
@@ -32,14 +32,17 @@
 import androidx.camera.core.imagecapture.Utils.SENSOR_TO_BUFFER
 import androidx.camera.core.imagecapture.Utils.TIMESTAMP
 import androidx.camera.core.imagecapture.Utils.WIDTH
+import androidx.camera.core.impl.Quirks
 import androidx.camera.core.impl.utils.Exif
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
 import androidx.camera.core.impl.utils.futures.Futures
 import androidx.camera.core.internal.CameraCaptureResultImageInfo
+import androidx.camera.core.internal.compat.quirk.IncorrectJpegMetadataQuirk
 import androidx.camera.core.internal.utils.ImageUtil.jpegImageToJpegByteArray
 import androidx.camera.core.processing.InternalImageProcessor
 import androidx.camera.testing.impl.AndroidUtil
 import androidx.camera.testing.impl.ExifUtil
+import androidx.camera.testing.impl.TestImageUtil.createA24ProblematicJpegByteArray
 import androidx.camera.testing.impl.TestImageUtil.createBitmap
 import androidx.camera.testing.impl.TestImageUtil.createJpegBytes
 import androidx.camera.testing.impl.TestImageUtil.createJpegFakeImageProxy
@@ -302,4 +305,43 @@
         val exif = Exif.createFromFileString(filePath)
         assertThat(exif.description).isEqualTo(EXIF_DESCRIPTION)
     }
+
+    @Test
+    fun canFixIncorrectJpegMetadataForA24Device(): Unit = runBlocking {
+        // Arrange.
+        // Force inject the quirk for the A24 incorrect JPEG metadata problem
+        val node =
+            ProcessingNode(mainThreadExecutor(), Quirks(listOf(IncorrectJpegMetadataQuirk())))
+        val nodeIn = ProcessingNode.In.of(ImageFormat.JPEG, ImageFormat.JPEG)
+        node.transform(nodeIn)
+        val takePictureCallback = FakeTakePictureCallback()
+
+        val processingRequest = ProcessingRequest(
+            { listOf() },
+            null,
+            Rect(0, 0, WIDTH, HEIGHT),
+            0,
+            /*jpegQuality=*/100,
+            SENSOR_TO_BUFFER,
+            takePictureCallback,
+            Futures.immediateFuture(null)
+        )
+        val imageIn = createJpegFakeImageProxy(
+            CameraCaptureResultImageInfo(CAMERA_CAPTURE_RESULT),
+            createA24ProblematicJpegByteArray(WIDTH, HEIGHT),
+            WIDTH,
+            HEIGHT
+        )
+        // Act.
+        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
+        // Act and return.
+        nodeIn.edge.accept(input)
+        // Assert: the output image is identical to the input.
+        val imageOut = takePictureCallback.getInMemoryResult()
+        val restoredJpeg = jpegImageToJpegByteArray(imageOut)
+
+        assertThat(getAverageDiff(createJpegBytes(WIDTH, HEIGHT), restoredJpeg)).isAtMost(
+            avgDiffTolerance
+        )
+    }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/Image2JpegBytes.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/Image2JpegBytes.java
index 3113c4d..6c1555d 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/Image2JpegBytes.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/Image2JpegBytes.java
@@ -22,7 +22,6 @@
 import static androidx.camera.core.ImageCapture.ERROR_UNKNOWN;
 import static androidx.camera.core.impl.utils.Exif.createFromInputStream;
 import static androidx.camera.core.impl.utils.TransformUtils.updateSensorToBufferTransform;
-import static androidx.camera.core.internal.utils.ImageUtil.jpegImageToJpegByteArray;
 
 import static java.util.Objects.requireNonNull;
 
@@ -35,7 +34,9 @@
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.ImageCaptureException;
 import androidx.camera.core.ImageProxy;
+import androidx.camera.core.impl.Quirks;
 import androidx.camera.core.impl.utils.Exif;
+import androidx.camera.core.internal.compat.workaround.JpegMetadataCorrector;
 import androidx.camera.core.internal.utils.ImageUtil;
 import androidx.camera.core.processing.Operation;
 import androidx.camera.core.processing.Packet;
@@ -50,6 +51,11 @@
  */
 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
 final class Image2JpegBytes implements Operation<Image2JpegBytes.In, Packet<byte[]>> {
+    private final JpegMetadataCorrector mJpegMetadataCorrector;
+
+    Image2JpegBytes(@NonNull Quirks quirks) {
+        mJpegMetadataCorrector = new JpegMetadataCorrector(quirks);
+    }
 
     @NonNull
     @Override
@@ -72,7 +78,7 @@
     private Packet<byte[]> processJpegImage(@NonNull Image2JpegBytes.In input) {
         Packet<ImageProxy> packet = input.getPacket();
         return Packet.of(
-                jpegImageToJpegByteArray(packet.getData()),
+                mJpegMetadataCorrector.jpegImageToJpegByteArray(packet.getData()),
                 requireNonNull(packet.getExif()),
                 JPEG,
                 packet.getSize(),
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 5b57a4f..ce77781 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
@@ -39,8 +39,10 @@
 import androidx.camera.core.ImageCaptureException;
 import androidx.camera.core.ImageProxy;
 import androidx.camera.core.Logger;
+import androidx.camera.core.impl.Quirks;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.internal.compat.quirk.DeviceQuirks;
+import androidx.camera.core.internal.compat.quirk.IncorrectJpegMetadataQuirk;
 import androidx.camera.core.internal.compat.quirk.LowMemoryQuirk;
 import androidx.camera.core.processing.Edge;
 import androidx.camera.core.processing.InternalImageProcessor;
@@ -76,6 +78,8 @@
     private Operation<Packet<byte[]>, Packet<ImageProxy>> mJpegBytes2Image;
     private Operation<Packet<ImageProxy>, Bitmap> mImage2Bitmap;
     private Operation<Packet<Bitmap>, Packet<Bitmap>> mBitmapEffect;
+    private final Quirks mQuirks;
+    private final boolean mHasIncorrectJpegMetadataQuirk;
 
     /**
      * @param blockingExecutor a executor that can be blocked by long running tasks. e.g.
@@ -83,7 +87,17 @@
      */
     @VisibleForTesting
     ProcessingNode(@NonNull Executor blockingExecutor) {
-        this(blockingExecutor, /*imageProcessor=*/null);
+        this(blockingExecutor, /*imageProcessor=*/null, DeviceQuirks.getAll());
+    }
+
+    @VisibleForTesting
+    ProcessingNode(@NonNull Executor blockingExecutor, @NonNull Quirks quirks) {
+        this(blockingExecutor, /*imageProcessor=*/null, quirks);
+    }
+
+    ProcessingNode(@NonNull Executor blockingExecutor,
+            @Nullable InternalImageProcessor imageProcessor) {
+        this(blockingExecutor, imageProcessor, DeviceQuirks.getAll());
     }
 
     /**
@@ -92,7 +106,8 @@
      * @param imageProcessor   external effect for post-processing.
      */
     ProcessingNode(@NonNull Executor blockingExecutor,
-            @Nullable InternalImageProcessor imageProcessor) {
+            @Nullable InternalImageProcessor imageProcessor,
+            @NonNull Quirks quirks) {
         boolean isLowMemoryDevice = DeviceQuirks.get(LowMemoryQuirk.class) != null;
         if (isLowMemoryDevice) {
             mBlockingExecutor = CameraXExecutors.newSequentialExecutor(blockingExecutor);
@@ -100,6 +115,8 @@
             mBlockingExecutor = blockingExecutor;
         }
         mImageProcessor = imageProcessor;
+        mQuirks = quirks;
+        mHasIncorrectJpegMetadataQuirk = quirks.contains(IncorrectJpegMetadataQuirk.class);
     }
 
     @NonNull
@@ -128,13 +145,14 @@
         );
 
         mInput2Packet = new ProcessingInput2Packet();
-        mImage2JpegBytes = new Image2JpegBytes();
+        mImage2JpegBytes = new Image2JpegBytes(mQuirks);
         mJpegBytes2CroppedBitmap = new JpegBytes2CroppedBitmap();
         mBitmap2JpegBytes = new Bitmap2JpegBytes();
         mJpegBytes2Disk = new JpegBytes2Disk();
         mJpegImage2Result = new JpegImage2Result();
         mImage2Bitmap = new Image2Bitmap();
-        if (inputEdge.getInputFormat() == YUV_420_888 || mImageProcessor != null) {
+        if (inputEdge.getInputFormat() == YUV_420_888 || mImageProcessor != null
+                || mHasIncorrectJpegMetadataQuirk) {
             // Convert JPEG bytes to ImageProxy for:
             // - YUV input: YUV -> JPEG -> ImageProxy
             // - Effects: JPEG -> Bitmap -> effect -> Bitmap -> JPEG -> ImageProxy
@@ -213,7 +231,8 @@
             throws ImageCaptureException {
         ProcessingRequest request = inputPacket.getProcessingRequest();
         Packet<ImageProxy> image = mInput2Packet.apply(inputPacket);
-        if ((image.getFormat() == YUV_420_888 || mBitmapEffect != null)
+        if ((image.getFormat() == YUV_420_888 || mBitmapEffect != null
+                || mHasIncorrectJpegMetadataQuirk)
                 && mInputEdge.getOutputFormat() == JPEG) {
             Packet<byte[]> jpegBytes = mImage2JpegBytes.apply(
                     Image2JpegBytes.In.of(image, request.getJpegQuality()));
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 e0d1a5c..cc00cef 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
@@ -102,6 +102,14 @@
     Timebase getTimebase();
 
     /**
+     * Returns the supported output formats of this camera.
+     *
+     * @return a set of supported output format, or an empty set if no output format is supported.
+     */
+    @NonNull
+    Set<Integer> getSupportedOutputFormats();
+
+    /**
      * Returns the supported resolutions of this camera based on the input image format.
      *
      * @param format an image format from {@link ImageFormat} or {@link PixelFormat}.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java
index 774ff31..7d101c05 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java
@@ -166,6 +166,12 @@
 
     @NonNull
     @Override
+    public Set<Integer> getSupportedOutputFormats() {
+        return mCameraInfoInternal.getSupportedOutputFormats();
+    }
+
+    @NonNull
+    @Override
     public List<Size> getSupportedResolutions(int format) {
         return mCameraInfoInternal.getSupportedResolutions(format);
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirks.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirks.java
index db756d6b..33c9bb4 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirks.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirks.java
@@ -44,6 +44,12 @@
     private DeviceQuirks() {
     }
 
+    /** Returns all device specific quirks loaded on the current device. */
+    @NonNull
+    public static Quirks getAll() {
+        return QUIRKS;
+    }
+
     /**
      * Retrieves a specific device {@link Quirk} instance given its type.
      *
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirksLoader.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirksLoader.java
index 4735c7c..44af3dad8 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirksLoader.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirksLoader.java
@@ -55,6 +55,9 @@
         if (LargeJpegImageQuirk.load()) {
             quirks.add(new LargeJpegImageQuirk());
         }
+        if (IncorrectJpegMetadataQuirk.load()) {
+            quirks.add(new IncorrectJpegMetadataQuirk());
+        }
 
         return quirks;
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/IncorrectJpegMetadataQuirk.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/IncorrectJpegMetadataQuirk.java
new file mode 100644
index 0000000..62b421d
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/IncorrectJpegMetadataQuirk.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.internal.compat.quirk;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.ImageProxy;
+import androidx.camera.core.impl.Quirk;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * <p>QuirkSummary
+ *     Bug Id: 309005680
+ *     Description: Quirk required to check whether the captured JPEG image has incorrect metadata.
+ *                  For example, Samsung A24 device has the problem and result in the captured
+ *                  image can't be parsed and saved successfully.
+ *     Device(s): Samsung Galaxy A24 device.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public final class IncorrectJpegMetadataQuirk implements Quirk {
+
+    private static final Set<String> SAMSUNG_DEVICES = new HashSet<>(Arrays.asList(
+            "A24" // Samsung Galaxy A24 series devices
+    ));
+
+    static boolean load() {
+        return isSamsungProblematicDevice();
+    }
+
+    private static boolean isSamsungProblematicDevice() {
+        return "Samsung".equalsIgnoreCase(Build.BRAND) && SAMSUNG_DEVICES.contains(
+                Build.DEVICE.toUpperCase(Locale.US));
+    }
+
+    /**
+     * Converts the image proxy to the byte array with correct JPEG metadata.
+     *
+     * <p>Some unexpected data exists in the head of the problematic JPEG images captured from
+     * the Samsung A24 device. Removing those data can fix the JPEG images.
+     */
+    @NonNull
+    public byte[] jpegImageToJpegByteArray(@NonNull ImageProxy imageProxy) {
+        ByteBuffer byteBuffer = imageProxy.getPlanes()[0].getBuffer();
+        byte[] bytes = new byte[byteBuffer.capacity()];
+        byteBuffer.rewind();
+        byteBuffer.get(bytes);
+
+        int copyStartPos = 0;
+
+        // Applies the solution only when the original JPEG data can't be correctly parsed to
+        // find the SOS marker position.
+        if (!canParseSosMarker(bytes)) {
+            int secondFfd8Position = findSecondFfd8Position(bytes);
+            if (secondFfd8Position != -1) {
+                copyStartPos = secondFfd8Position;
+            } else {
+                return bytes;
+            }
+        }
+
+        return Arrays.copyOfRange(bytes, copyStartPos, byteBuffer.limit());
+    }
+
+    /**
+     * Returns whether the JFIF SOS marker can be correctly parsed from the input JPEG byte data.
+     */
+    private boolean canParseSosMarker(@NonNull byte[] bytes) {
+        // Parses the JFIF segments from the start of the JPEG image data
+        int markPosition = 0x2;
+        while (true) {
+            // Breaks the while-loop and return false if the mark byte can't be correctly found.
+            if (markPosition + 4 > bytes.length || bytes[markPosition] != ((byte) 0xff)) {
+                return false;
+            }
+            // Breaks the while-loop when finding the SOS (FF DA) mark
+            if (bytes[markPosition] == ((byte) 0xff) && bytes[markPosition + 1] == ((byte) 0xda)) {
+                return true;
+            }
+            int segmentLength =
+                    ((bytes[markPosition + 2] & 0xff) << 8) | (bytes[markPosition + 3] & 0xff);
+            markPosition += segmentLength + 2;
+        }
+    }
+
+    /**
+     * Returns the second FFD8 position.
+     *
+     * @param bytes the JPEG byte array data.
+     * @return the second FFD8 position if it can be found. Otherwise, returns -1.
+     */
+    private int findSecondFfd8Position(@NonNull byte[] bytes) {
+        // Starts from the position 2 to skip the first FFD8
+        int position = 2;
+
+        while (true) {
+            if (position + 1 > bytes.length) {
+                break;
+            }
+            // Find and return the second FFD8 position
+            if (bytes[position] == ((byte) 0xff)
+                    && bytes[position + 1] == ((byte) 0xd8)) {
+                return position;
+            }
+            position++;
+        }
+
+        return -1;
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/LargeJpegImageQuirk.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/LargeJpegImageQuirk.java
index 9001ace..438acf5 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/LargeJpegImageQuirk.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/LargeJpegImageQuirk.java
@@ -18,6 +18,7 @@
 
 import android.os.Build;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.impl.Quirk;
 
@@ -33,11 +34,15 @@
  *                  0's padding data. For example, Samsung A5 (2017) series devices have the
  *                  problem and result in the output JPEG image to be extremely large (about 32 MB).
  *     Device(s): Samsung Galaxy A5 (2017), A52, A70, A71, A72, M51, S7, S22, S22+ series devices
- *                and Vivo S16 device.
+ *                and Vivo S16 device. This issue might also happen on some other Samsung devices
+ *                . Therefore, a generic rule is added to force check the invalid JPEG data if
+ *                the captured image size is larger than 10 MB.
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public final class LargeJpegImageQuirk implements Quirk {
 
+    private static final int INVALID_JPEG_DATA_CHECK_THRESHOLD = 10_000_000; // 10 MB
+
     private static final Set<String> SAMSUNG_DEVICE_MODELS = new HashSet<>(Arrays.asList(
             // Samsung Galaxy A5 series devices
             "SM-A520F",
@@ -87,7 +92,7 @@
     ));
 
     static boolean load() {
-        return isSamsungProblematicDevice() || isVivoProblematicDevice();
+        return isSamsungDevice() || isVivoProblematicDevice();
     }
 
     private static boolean isSamsungProblematicDevice() {
@@ -95,8 +100,25 @@
                 Build.MODEL.toUpperCase(Locale.US));
     }
 
+    private static boolean isSamsungDevice() {
+        return "Samsung".equalsIgnoreCase(Build.BRAND);
+    }
+
     private static boolean isVivoProblematicDevice() {
         return "Vivo".equalsIgnoreCase(Build.BRAND) && VIVO_DEVICE_MODELS.contains(
                 Build.MODEL.toUpperCase(Locale.US));
     }
+
+    /**
+     * Return {@code true} if there might be invalid JPEG data contained in the bytes array.
+     */
+    public boolean shouldCheckInvalidJpegData(@NonNull byte[] bytes) {
+        // For the confirmed problematic devices, always check the invalid data
+        if (isSamsungProblematicDevice() || isVivoProblematicDevice()) {
+            return true;
+        } else {
+            // For other devices, check the invalid data if the image size is larger than 10 MB
+            return bytes.length > INVALID_JPEG_DATA_CHECK_THRESHOLD;
+        }
+    }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/workaround/InvalidJpegDataParser.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/workaround/InvalidJpegDataParser.java
index 4a0d527..9f911fd 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/workaround/InvalidJpegDataParser.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/workaround/InvalidJpegDataParser.java
@@ -29,7 +29,7 @@
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class InvalidJpegDataParser {
-    private final boolean mHasQuirk = DeviceQuirks.get(LargeJpegImageQuirk.class) != null;
+    private final LargeJpegImageQuirk mQuirk = DeviceQuirks.get(LargeJpegImageQuirk.class);
 
     /**
      * Returns the valid data length of the input JPEG byte data array which is determined by the
@@ -38,7 +38,7 @@
      * <p>Returns the original byte array length when quirk doesn't exist or EOI can't be found.
      */
     public int getValidDataLength(@NonNull byte[] bytes) {
-        if (!mHasQuirk) {
+        if (mQuirk == null || !mQuirk.shouldCheckInvalidJpegData(bytes)) {
             return bytes.length;
         }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/workaround/JpegMetadataCorrector.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/workaround/JpegMetadataCorrector.java
new file mode 100644
index 0000000..569c184
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/workaround/JpegMetadataCorrector.java
@@ -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.camera.core.internal.compat.workaround;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.ImageProxy;
+import androidx.camera.core.impl.Quirks;
+import androidx.camera.core.internal.compat.quirk.IncorrectJpegMetadataQuirk;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Workaround to correct the JPEG metadata for specific problematic devices.
+ *
+ * @see IncorrectJpegMetadataQuirk
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class JpegMetadataCorrector {
+    private final IncorrectJpegMetadataQuirk mQuirk;
+
+    /**
+     * Constructor
+     */
+    public JpegMetadataCorrector(@NonNull Quirks quirks) {
+        mQuirk = quirks.get(IncorrectJpegMetadataQuirk.class);
+    }
+
+    /**
+     * Returns whether JPEG file metadata needs to be corrected or not.
+     */
+    public boolean needCorrectJpegMetadata() {
+        return mQuirk != null;
+    }
+
+    /**
+     * Converts the image proxy to the byte array with correct JPEG metadata.
+     */
+    @NonNull
+    public byte[] jpegImageToJpegByteArray(@NonNull ImageProxy image) {
+        if (mQuirk == null) {
+            ImageProxy.PlaneProxy[] planes = image.getPlanes();
+            ByteBuffer buffer = planes[0].getBuffer();
+            byte[] data = new byte[buffer.capacity()];
+            buffer.rewind();
+            buffer.get(data);
+            return data;
+        } else {
+            return mQuirk.jpegImageToJpegByteArray(image);
+        }
+    }
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/Image2JpegBytesTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/Image2JpegBytesTest.kt
index 707f5c6..13253c9 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/Image2JpegBytesTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/Image2JpegBytesTest.kt
@@ -25,6 +25,7 @@
 import androidx.camera.core.imagecapture.Utils.ROTATION_DEGREES
 import androidx.camera.core.imagecapture.Utils.SENSOR_TO_BUFFER
 import androidx.camera.core.imagecapture.Utils.WIDTH
+import androidx.camera.core.internal.compat.quirk.DeviceQuirks
 import androidx.camera.core.processing.Packet
 import androidx.camera.testing.impl.ExifUtil.createExif
 import androidx.camera.testing.impl.TestImageUtil.createJpegBytes
@@ -45,7 +46,7 @@
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 class Image2JpegBytesTest {
 
-    private val operation = Image2JpegBytes()
+    private val operation = Image2JpegBytes(DeviceQuirks.getAll())
 
     @Test
     fun processJpegImage_assertOutput() {
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 0e0be4b..3c85deb 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
@@ -21,6 +21,7 @@
 import android.os.Build
 import android.os.Looper.getMainLooper
 import androidx.camera.core.ImageCaptureException
+import androidx.camera.core.imagecapture.Utils.CAMERA_CAPTURE_RESULT
 import androidx.camera.core.imagecapture.Utils.HEIGHT
 import androidx.camera.core.imagecapture.Utils.OUTPUT_FILE_OPTIONS
 import androidx.camera.core.imagecapture.Utils.ROTATION_DEGREES
@@ -30,6 +31,8 @@
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.isSequentialExecutor
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
 import androidx.camera.core.impl.utils.futures.Futures
+import androidx.camera.core.internal.CameraCaptureResultImageInfo
+import androidx.camera.testing.impl.TestImageUtil.createA24ProblematicJpegByteArray
 import androidx.camera.testing.impl.TestImageUtil.createJpegBytes
 import androidx.camera.testing.impl.TestImageUtil.createJpegFakeImageProxy
 import androidx.camera.testing.impl.fakes.FakeImageInfo
@@ -54,7 +57,7 @@
 
     private lateinit var processingNodeIn: ProcessingNode.In
 
-    private val node = ProcessingNode(mainThreadExecutor())
+    private var node = ProcessingNode(mainThreadExecutor())
 
     @Before
     fun setUp() {
@@ -191,4 +194,35 @@
             ).isTrue()
         }
     }
+
+    @Test
+    fun canProcessOnDiskCaptureForA24ProblematicJpegMetadata() {
+        setStaticField(Build::class.java, "BRAND", "SAMSUNG")
+        setStaticField(Build::class.java, "DEVICE", "a24")
+
+        // Creates the ProcessingNode after updating the device name to load the correct quirks
+        node = ProcessingNode(mainThreadExecutor())
+
+        processingNodeIn = ProcessingNode.In.of(ImageFormat.JPEG, ImageFormat.JPEG)
+        node.transform(processingNodeIn)
+
+        // Arrange: create an invalid ImageProxy.
+        val takePictureCallback = FakeTakePictureCallback()
+        val brokenJpegByteArray = createA24ProblematicJpegByteArray(WIDTH, HEIGHT)
+        val image = createJpegFakeImageProxy(
+            CameraCaptureResultImageInfo(CAMERA_CAPTURE_RESULT),
+            brokenJpegByteArray,
+            WIDTH,
+            HEIGHT
+        )
+        val processingRequest = createProcessingRequest(takePictureCallback)
+        val input = ProcessingNode.InputPacket.of(processingRequest, image)
+
+        // Act: send input to the edge and wait for callback
+        processingNodeIn.edge.accept(input)
+        shadowOf(getMainLooper()).idle()
+
+        // Assert: can process the problematic A24 JPEG byte array successfully.
+        assertThat(takePictureCallback.processFailure).isNull()
+    }
 }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/quirk/DeviceQuirks.java b/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/quirk/DeviceQuirks.java
index 63fc7fc..b2bc66a 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/quirk/DeviceQuirks.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/quirk/DeviceQuirks.java
@@ -20,6 +20,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.impl.Quirk;
+import androidx.camera.core.impl.Quirks;
 
 import java.util.List;
 
@@ -39,6 +40,13 @@
     private DeviceQuirks() {
     }
 
+    /** Returns all device specific quirks loaded on the current device. */
+    @RequiresApi(21)
+    @NonNull
+    public static Quirks getAll() {
+        return new Quirks(DeviceQuirksLoader.loadQuirks());
+    }
+
     /**
      * Retrieves a specific device {@link Quirk} instance given its type.
      *
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/workaround/JpegMetadataCorrectorTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/workaround/JpegMetadataCorrectorTest.kt
new file mode 100644
index 0000000..72ca4be
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/workaround/JpegMetadataCorrectorTest.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.camera.core.internal.compat.workaround
+
+import android.graphics.BitmapFactory
+import android.os.Build
+import androidx.camera.core.internal.compat.quirk.DeviceQuirks
+import androidx.camera.testing.impl.TestImageUtil
+import androidx.camera.testing.impl.TestImageUtil.createA24ProblematicJpegByteArray
+import androidx.camera.testing.impl.fakes.FakeImageInfo
+import androidx.testutils.assertThrows
+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
+import org.robolectric.util.ReflectionHelpers
+
+private const val WIDTH = 640
+private const val HEIGHT = 480
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.O)
+class JpegMetadataCorrectorTest {
+
+    @Test
+    fun needCorrectJpegMetadataOnSamsungA24() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "SAMSUNG")
+        ReflectionHelpers.setStaticField(Build::class.java, "DEVICE", "a24")
+        assertThat(JpegMetadataCorrector(DeviceQuirks.getAll()).needCorrectJpegMetadata()).isTrue()
+    }
+
+    @Test
+    fun doesNotNeedCorrectJpegMetadataOnSamsungA23() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "SAMSUNG")
+        ReflectionHelpers.setStaticField(Build::class.java, "DEVICE", "a23")
+        assertThat(JpegMetadataCorrector(DeviceQuirks.getAll()).needCorrectJpegMetadata()).isFalse()
+    }
+
+    @Test
+    fun canCorrectHeaderData_whenJpegMetadataIsIncorrect() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "SAMSUNG")
+        ReflectionHelpers.setStaticField(Build::class.java, "DEVICE", "a24")
+
+        val brokenJpegByteArray = createA24ProblematicJpegByteArray(WIDTH, HEIGHT)
+        assertThrows<RuntimeException> {
+            BitmapFactory.decodeByteArray(brokenJpegByteArray, 0, brokenJpegByteArray.size)
+        }
+
+        val fakeImageProxy = TestImageUtil.createJpegFakeImageProxy(
+            FakeImageInfo(),
+            brokenJpegByteArray,
+            WIDTH,
+            HEIGHT
+        )
+        val correctedJpegByteArray =
+            JpegMetadataCorrector(DeviceQuirks.getAll()).jpegImageToJpegByteArray(fakeImageProxy)
+        assertThat(
+            BitmapFactory.decodeByteArray(
+                correctedJpegByteArray,
+                0,
+                correctedJpegByteArray.size
+            )
+        ).isNotNull()
+    }
+
+    @Test
+    fun canKeepData_whenJpegMetadataIsCorrect() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "SAMSUNG")
+        ReflectionHelpers.setStaticField(Build::class.java, "DEVICE", "a24")
+
+        val jpegByteArray = TestImageUtil.createJpegBytes(WIDTH, HEIGHT)
+        val fakeImageProxy =
+            TestImageUtil.createJpegFakeImageProxy(FakeImageInfo(), jpegByteArray, WIDTH, HEIGHT)
+        val resultJpegByteArray =
+            JpegMetadataCorrector(DeviceQuirks.getAll()).jpegImageToJpegByteArray(fakeImageProxy)
+        assertThat(
+            BitmapFactory.decodeByteArray(
+                resultJpegByteArray,
+                0,
+                resultJpegByteArray.size
+            )
+        ).isNotNull()
+    }
+}
diff --git a/camera/camera-lifecycle/build.gradle b/camera/camera-lifecycle/build.gradle
index b224fbb..515d2da 100644
--- a/camera/camera-lifecycle/build.gradle
+++ b/camera/camera-lifecycle/build.gradle
@@ -51,7 +51,7 @@
     }
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.kotlinCoroutinesAndroid)
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
     androidTestImplementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
     androidTestImplementation(project(":internal-testutils-truth"))
     androidTestImplementation("org.jetbrains.kotlinx:atomicfu:0.13.1")
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 dab206c..9d3e273 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
@@ -269,6 +269,13 @@
         return mTimebase;
     }
 
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @NonNull
+    @Override
+    public Set<Integer> getSupportedOutputFormats() {
+        return mSupportedResolutionMap.keySet();
+    }
+
     @NonNull
     @Override
     public List<Size> getSupportedResolutions(int format) {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/TestImageUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/TestImageUtil.java
index 8bcd510..43219e3 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/TestImageUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/TestImageUtil.java
@@ -85,11 +85,21 @@
     public static FakeImageProxy createJpegFakeImageProxy(@NonNull ImageInfo imageInfo,
             @NonNull byte[] jpegBytes) {
         Bitmap bitmap = decodeByteArray(jpegBytes, 0, jpegBytes.length);
+        return createJpegFakeImageProxy(imageInfo, jpegBytes, bitmap.getWidth(),
+                bitmap.getHeight());
+    }
+
+    /**
+     * Creates a {@link FakeImageProxy} from JPEG bytes.
+     */
+    @NonNull
+    public static FakeImageProxy createJpegFakeImageProxy(@NonNull ImageInfo imageInfo,
+            @NonNull byte[] jpegBytes, int width, int height) {
         FakeImageProxy image = new FakeImageProxy(imageInfo);
         image.setFormat(JPEG);
         image.setPlanes(new FakeJpegPlaneProxy[]{new FakeJpegPlaneProxy(jpegBytes)});
-        image.setWidth(bitmap.getWidth());
-        image.setHeight(bitmap.getHeight());
+        image.setWidth(width);
+        image.setHeight(height);
         return image;
     }
 
@@ -114,6 +124,22 @@
     }
 
     /**
+     * Generates a A24 problematic JPEG image.
+     */
+    @NonNull
+    public static byte[] createA24ProblematicJpegByteArray(int width, int height) {
+        byte[] incorrectHeaderByteData =
+                new byte[]{(byte) 0xff, (byte) 0xd8, (byte) 0xff, (byte) 0xe1, (byte) 0xff,
+                        (byte) 0x7c, (byte) 0x45, (byte) 0x78, (byte) 0x69, (byte) 0x66,
+                        (byte) 0x00, (byte) 0x00};
+        byte[] jpegBytes = createJpegBytes(width, height);
+        byte[] result = new byte[incorrectHeaderByteData.length + jpegBytes.length];
+        System.arraycopy(incorrectHeaderByteData, 0, result, 0, incorrectHeaderByteData.length);
+        System.arraycopy(jpegBytes, 0, result, incorrectHeaderByteData.length, jpegBytes.length);
+        return result;
+    }
+
+    /**
      * Generates a {@link Bitmap} image and paints it with 4 color blocks.
      */
     @NonNull
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 f232a73..b9c6c1c 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
@@ -17,6 +17,9 @@
 package androidx.camera.testing.fakes;
 
 
+import static android.graphics.ImageFormat.JPEG;
+import static android.graphics.ImageFormat.JPEG_R;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Build;
@@ -33,7 +36,9 @@
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
@@ -75,6 +80,17 @@
     }
 
     @Test
+    public void canRetrieveSupportedOutputFormats() {
+        mFakeCameraInfo.setSupportedResolutions(JPEG, new ArrayList<>());
+        mFakeCameraInfo.setSupportedResolutions(JPEG_R, new ArrayList<>());
+
+        Set<Integer> formats = new HashSet<>();
+        formats.add(JPEG);
+        formats.add(JPEG_R);
+        assertThat(mFakeCameraInfo.getSupportedOutputFormats()).containsExactlyElementsIn(formats);
+    }
+
+    @Test
     public void canRetrieveSupportedFpsRanges() {
         assertThat(mFakeCameraInfo.getSupportedFrameRateRanges()).isNotEmpty();
 
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
index c514c78..2a86fcf 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
@@ -1291,8 +1291,8 @@
         }!!.await(timeoutMillis, TimeUnit.MILLISECONDS)).isTrue()
     }
 
-    override fun accept(event: VideoRecordEvent?) {
-        when (event) {
+    override fun accept(value: VideoRecordEvent) {
+        when (value) {
             is VideoRecordEvent.Status -> {
                 synchronized(this) {
                     countDown?.countDown()
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java
index 910c92b..4af961b 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java
@@ -104,6 +104,9 @@
         if (ExtraSupportedQualityQuirk.load()) {
             quirks.add(new ExtraSupportedQualityQuirk());
         }
+        if (SignalEosOutputBufferNotComeQuirk.load()) {
+            quirks.add(new SignalEosOutputBufferNotComeQuirk());
+        }
 
         return quirks;
     }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/SignalEosOutputBufferNotComeQuirk.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/SignalEosOutputBufferNotComeQuirk.java
new file mode 100644
index 0000000..bcba3a3
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/SignalEosOutputBufferNotComeQuirk.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.compat.quirk;
+
+import android.media.MediaCodec;
+import android.os.Build;
+
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.Quirk;
+
+/**
+ * <p>QuirkSummary
+ *     Bug Id: b/317366465
+ *     Description: Quirk denotes that the MediaCodec doesn't send an end of stream buffer callback
+ *                  after {@link MediaCodec#signalEndOfInputStream()} is called.
+ *                  <p>On Nokia 1, it happens when the camera repeating is stopped while recording.
+ *                  E.g. lifecycle is stopped or VideoCapture is unbound while recording. It is
+ *                  not 100% reproducible.
+ *     Device(s): Nokia 1
+ */
+@RequiresApi(21)
+public class SignalEosOutputBufferNotComeQuirk implements Quirk {
+
+    static boolean load() {
+        return isNokia1();
+    }
+
+    private static boolean isNokia1() {
+        return "Nokia".equalsIgnoreCase(Build.BRAND) && "Nokia 1".equalsIgnoreCase(Build.MODEL);
+    }
+}
+
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java
index ad2d596..8e9932f 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java
@@ -16,6 +16,7 @@
 
 package androidx.camera.video.internal.encoder;
 
+import static androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor;
 import static androidx.camera.video.internal.utils.CodecUtil.createCodec;
 import static androidx.camera.video.internal.encoder.EncoderImpl.InternalState.CONFIGURED;
 import static androidx.camera.video.internal.encoder.EncoderImpl.InternalState.ERROR;
@@ -57,6 +58,7 @@
 import androidx.camera.video.internal.compat.quirk.CodecStuckOnFlushQuirk;
 import androidx.camera.video.internal.compat.quirk.DeviceQuirks;
 import androidx.camera.video.internal.compat.quirk.EncoderNotUsePersistentInputSurfaceQuirk;
+import androidx.camera.video.internal.compat.quirk.SignalEosOutputBufferNotComeQuirk;
 import androidx.camera.video.internal.compat.quirk.StopCodecAfterSurfaceRemovalCrashMediaServerQuirk;
 import androidx.camera.video.internal.compat.quirk.VideoEncoderSuspendDoesNotIncludeSuspendTimeQuirk;
 import androidx.camera.video.internal.workaround.VideoTimebaseConverter;
@@ -150,6 +152,7 @@
     private static final long NO_LIMIT_LONG = Long.MAX_VALUE;
     private static final Range<Long> NO_RANGE = Range.create(NO_LIMIT_LONG, NO_LIMIT_LONG);
     private static final long STOP_TIMEOUT_MS = 1000L;
+    private static final long SIGNAL_EOS_TIMEOUT_MS = 1000L;
 
     @SuppressWarnings("WeakerAccess") // synthetic accessor
     final String mTag;
@@ -209,6 +212,8 @@
     private boolean mIsFlushedAfterEndOfStream = false;
     private boolean mSourceStoppedSignalled = false;
     boolean mMediaCodecEosSignalled = false;
+    @Nullable
+    private Future<?> mSignalEosTimeoutFuture;
 
     /**
      * Creates the encoder with a {@link EncoderConfig}
@@ -306,6 +311,10 @@
             mStopTimeoutFuture.cancel(true);
             mStopTimeoutFuture = null;
         }
+        if (mSignalEosTimeoutFuture != null) {
+            mSignalEosTimeoutFuture.cancel(false);
+            mSignalEosTimeoutFuture = null;
+        }
         if (mMediaCodecCallback != null) {
             mMediaCodecCallback.stop();
         }
@@ -500,7 +509,7 @@
                         // times out, stop the codec so that the Encoder can at least be stopped.
                         // Set mDataStopTimeStamp to be null in order to catch this issue in test.
                         mStopTimeoutFuture =
-                                CameraXExecutors.mainThreadExecutor().schedule(
+                                mainThreadExecutor().schedule(
                                         () -> mEncoderExecutor.execute(() -> {
                                             if (mPendingCodecStop) {
                                                 Logger.w(mTag,
@@ -530,6 +539,7 @@
     @SuppressWarnings("WeakerAccess") // synthetic accessor
     @ExecutedBy("mEncoderExecutor")
     void signalCodecStop() {
+        Logger.d(mTag, "signalCodecStop");
         if (mEncoderInput instanceof ByteBufferInput) {
             ((ByteBufferInput) mEncoderInput).setActive(false);
             // Wait for all issued input buffer done to avoid input loss.
@@ -541,6 +551,7 @@
                     mEncoderExecutor);
         } else if (mEncoderInput instanceof SurfaceInput) {
             try {
+                addSignalEosTimeoutIfNeeded();
                 mMediaCodec.signalEndOfInputStream();
                 mMediaCodecEosSignalled = true;
             } catch (MediaCodec.CodecException e) {
@@ -727,6 +738,20 @@
     }
 
     @ExecutedBy("mEncoderExecutor")
+    private void addSignalEosTimeoutIfNeeded() {
+        if (DeviceQuirks.get(SignalEosOutputBufferNotComeQuirk.class) != null) {
+            MediaCodecCallback codecCallback = mMediaCodecCallback;
+            Executor executor = mEncoderExecutor;
+            if (mSignalEosTimeoutFuture != null) {
+                mSignalEosTimeoutFuture.cancel(false);
+            }
+            mSignalEosTimeoutFuture = mainThreadExecutor().schedule(
+                    () -> executor.execute(codecCallback::reachEndData),
+                    SIGNAL_EOS_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+    }
+
+    @ExecutedBy("mEncoderExecutor")
     private void signalEndOfInputStream() {
         Futures.addCallback(acquireInputBuffer(),
                 new FutureCallback<InputBuffer>() {
@@ -822,6 +847,7 @@
     @SuppressWarnings("WeakerAccess") // synthetic accessor
     @ExecutedBy("mEncoderExecutor")
     void stopMediaCodec(@Nullable Runnable afterStop) {
+        Logger.d(mTag, "stopMediaCodec");
         /*
          * MediaCodec#stop will free all its input/output ByteBuffers. Therefore, before calling
          * MediaCodec#stop, it must ensure all dispatched EncodedData(output ByteBuffers) and
@@ -1163,18 +1189,7 @@
 
                         // Handle end of stream
                         if (!mHasEndData && isEndOfStream(bufferInfo)) {
-                            mHasEndData = true;
-                            stopMediaCodec(() -> {
-                                if (mState == ERROR) {
-                                    // Error occur during stopping.
-                                    return;
-                                }
-                                try {
-                                    executor.execute(encoderCallback::onEncodeStop);
-                                } catch (RejectedExecutionException e) {
-                                    Logger.e(mTag, "Unable to post to the supplied executor.", e);
-                                }
-                            });
+                            reachEndData();
                         }
                         break;
                     case CONFIGURED:
@@ -1189,6 +1204,35 @@
         }
 
         @ExecutedBy("mEncoderExecutor")
+        void reachEndData() {
+            if (mHasEndData) {
+                return;
+            }
+            mHasEndData = true;
+            if (mSignalEosTimeoutFuture != null) {
+                mSignalEosTimeoutFuture.cancel(false);
+                mSignalEosTimeoutFuture = null;
+            }
+            EncoderCallback encoderCallback;
+            Executor executor;
+            synchronized (mLock) {
+                encoderCallback = mEncoderCallback;
+                executor = mEncoderCallbackExecutor;
+            }
+            stopMediaCodec(() -> {
+                if (mState == ERROR) {
+                    // Error occur during stopping.
+                    return;
+                }
+                try {
+                    executor.execute(encoderCallback::onEncodeStop);
+                } catch (RejectedExecutionException e) {
+                    Logger.e(mTag, "Unable to post to the supplied executor.", e);
+                }
+            });
+        }
+
+        @ExecutedBy("mEncoderExecutor")
         @NonNull
         private BufferInfo resolveOutputBufferInfo(@NonNull BufferInfo bufferInfo) {
             long adjustedTimeUs = getAdjustedTimeUs(bufferInfo);
diff --git a/camera/camera-view/build.gradle b/camera/camera-view/build.gradle
index c5dbaba..3ffab6d 100644
--- a/camera/camera-view/build.gradle
+++ b/camera/camera-view/build.gradle
@@ -35,7 +35,7 @@
     api(project(":camera:camera-core"))
     api(project(":camera:camera-video"))
     implementation(project(":camera:camera-lifecycle"))
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
     implementation(libs.guavaListenableFuture)
     implementation("androidx.core:core:1.3.2")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
diff --git a/camera/camera-viewfinder-core/build.gradle b/camera/camera-viewfinder-core/build.gradle
index b47ad6b..cfa03df 100644
--- a/camera/camera-viewfinder-core/build.gradle
+++ b/camera/camera-viewfinder-core/build.gradle
@@ -32,7 +32,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.2.0")
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
     implementation(libs.guavaListenableFuture)
     implementation("androidx.core:core:1.7.0")
     implementation("androidx.concurrent:concurrent-futures:1.1.0")
diff --git a/camera/camera-viewfinder/build.gradle b/camera/camera-viewfinder/build.gradle
index d7e9211..7ab055e 100644
--- a/camera/camera-viewfinder/build.gradle
+++ b/camera/camera-viewfinder/build.gradle
@@ -32,7 +32,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.2.0")
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
     implementation(libs.guavaListenableFuture)
     implementation("androidx.core:core:1.7.0")
     implementation("androidx.concurrent:concurrent-futures:1.1.0")
diff --git a/camera/integration-tests/avsynctestapp/build.gradle b/camera/integration-tests/avsynctestapp/build.gradle
index 14e4cc2..7bec0da 100644
--- a/camera/integration-tests/avsynctestapp/build.gradle
+++ b/camera/integration-tests/avsynctestapp/build.gradle
@@ -64,7 +64,7 @@
     }
 
     // Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
 
     // Testing framework
     testImplementation(libs.kotlinCoroutinesTest)
diff --git a/camera/integration-tests/coretestapp/build.gradle b/camera/integration-tests/coretestapp/build.gradle
index 59cef71..4dda00a 100644
--- a/camera/integration-tests/coretestapp/build.gradle
+++ b/camera/integration-tests/coretestapp/build.gradle
@@ -98,7 +98,7 @@
     debugImplementation(libs.testRunner)
 
     // Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
 
     // Testing framework
     androidTestImplementation(libs.testCore)
diff --git a/camera/integration-tests/diagnosetestapp/build.gradle b/camera/integration-tests/diagnosetestapp/build.gradle
index 28ebe70..bb95910 100644
--- a/camera/integration-tests/diagnosetestapp/build.gradle
+++ b/camera/integration-tests/diagnosetestapp/build.gradle
@@ -65,7 +65,7 @@
     compileOnly(libs.kotlinCompiler)
 
     // Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
 
     // Testing framework
     androidTestImplementation(libs.testExtJunit)
diff --git a/camera/integration-tests/extensionstestapp/build.gradle b/camera/integration-tests/extensionstestapp/build.gradle
index 3137f0a..bfa17a5 100644
--- a/camera/integration-tests/extensionstestapp/build.gradle
+++ b/camera/integration-tests/extensionstestapp/build.gradle
@@ -74,7 +74,7 @@
     implementation("androidx.viewpager2:viewpager2:1.0.0")
 
     // Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
diff --git a/camera/integration-tests/timingtestapp/build.gradle b/camera/integration-tests/timingtestapp/build.gradle
index 8eb3d62..840066e 100644
--- a/camera/integration-tests/timingtestapp/build.gradle
+++ b/camera/integration-tests/timingtestapp/build.gradle
@@ -67,7 +67,7 @@
     implementation(libs.kotlinCoroutinesAndroid)
 
     // Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
 
     // Testing framework
     androidTestImplementation(project(":concurrent:concurrent-futures"))
diff --git a/camera/integration-tests/uiwidgetstestapp/build.gradle b/camera/integration-tests/uiwidgetstestapp/build.gradle
index 598b557..98a7d6a 100644
--- a/camera/integration-tests/uiwidgetstestapp/build.gradle
+++ b/camera/integration-tests/uiwidgetstestapp/build.gradle
@@ -99,7 +99,7 @@
     androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.1.1'
 
     // Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
 
     // Testing framework
     androidTestImplementation(libs.testExtJunit)
@@ -134,7 +134,7 @@
     androidTestImplementation(project(":compose:ui:ui-graphics"))
     androidTestImplementation(project(":compose:ui:ui-unit"))
     androidTestImplementation(project(":compose:ui:ui-text"))
-    androidTestImplementation(project(":collection:collection"))
+    androidTestImplementation("androidx.collection:collection:1.4.0")
     // Needed for createComposeRule, but not createAndroidComposeRule:
     debugImplementation(project(":compose:ui:ui-test-manifest"))
 }
diff --git a/camera/integration-tests/viewfindertestapp/build.gradle b/camera/integration-tests/viewfindertestapp/build.gradle
index f882ac2..178d84d 100644
--- a/camera/integration-tests/viewfindertestapp/build.gradle
+++ b/camera/integration-tests/viewfindertestapp/build.gradle
@@ -59,7 +59,7 @@
     compileOnly(libs.kotlinCompiler)
 
     // Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
 
     // Testing framework
     androidTestImplementation(libs.testExtJunit)
diff --git a/camera/integration-tests/viewtestapp/build.gradle b/camera/integration-tests/viewtestapp/build.gradle
index 8cdc4c2..017a1cd 100644
--- a/camera/integration-tests/viewtestapp/build.gradle
+++ b/camera/integration-tests/viewtestapp/build.gradle
@@ -87,7 +87,7 @@
     implementation("androidx.activity:activity-compose:1.3.1")
 
     // Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
 
     // Testing framework
     androidTestImplementation(libs.testExtJunit)
diff --git a/car/app/app-automotive/build.gradle b/car/app/app-automotive/build.gradle
index aaf75d3..896c8db 100644
--- a/car/app/app-automotive/build.gradle
+++ b/car/app/app-automotive/build.gradle
@@ -31,7 +31,7 @@
 dependencies {
     api(project(":car:app:app"))
     api(libs.guavaListenableFuture)
-    api(project(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
     implementation(libs.guavaAndroid)
     implementation("androidx.concurrent:concurrent-futures:1.1.0")
     implementation("androidx.fragment:fragment:1.3.0")
diff --git a/car/app/app-testing/build.gradle b/car/app/app-testing/build.gradle
index 0abc8c7..b003f88 100644
--- a/car/app/app-testing/build.gradle
+++ b/car/app/app-testing/build.gradle
@@ -34,7 +34,7 @@
     implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
     implementation 'androidx.annotation:annotation:1.1.0'
     implementation(libs.robolectric)
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
 
     testImplementation(project(":car:app:app-projected"))
     testImplementation(libs.junit)
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 62a19f5..be1aa88 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -855,14 +855,11 @@
 package androidx.car.app.mediaextensions {
 
   public final class MediaBrowserExtras {
-    field public static final String KEY_HINT_HOST_PACKAGE_NAME = "androidx.car.app.mediaextensions.KEY_HINT_HOST_PACKAGE_NAME";
-    field public static final String KEY_HINT_VIEW_HEIGHT_PIXELS = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_HEIGHT_PIXELS";
     field public static final String KEY_HINT_VIEW_MAX_CATEGORY_GRID_ITEMS_COUNT_PER_ROW = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_MAX_CATEGORY_GRID_ITEMS_COUNT_PER_ROW";
     field public static final String KEY_HINT_VIEW_MAX_CATEGORY_LIST_ITEMS_COUNT_PER_ROW = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_MAX_CATEGORY_LIST_ITEMS_COUNT_PER_ROW";
     field public static final String KEY_HINT_VIEW_MAX_GRID_ITEMS_COUNT_PER_ROW = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_MAX_GRID_ITEMS_COUNT_PER_ROW";
     field public static final String KEY_HINT_VIEW_MAX_ITEMS_WHILE_RESTRICTED = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_MAX_ITEMS_WHILE_RESTRICTED";
     field public static final String KEY_HINT_VIEW_MAX_LIST_ITEMS_COUNT_PER_ROW = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_MAX_LIST_ITEMS_COUNT_PER_ROW";
-    field public static final String KEY_HINT_VIEW_WIDTH_PIXELS = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_WIDTH_PIXELS";
     field public static final String KEY_ROOT_HINT_MAX_QUEUE_ITEMS_WHILE_RESTRICTED = "androidx.car.app.mediaextensions.KEY_ROOT_HINT_MAX_QUEUE_ITEMS_WHILE_RESTRICTED";
     field public static final String KEY_ROOT_HINT_MEDIA_SESSION_API = "androidx.car.app.mediaextensions.KEY_ROOT_HINT_MEDIA_SESSION_API";
   }
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 62a19f5..be1aa88 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -855,14 +855,11 @@
 package androidx.car.app.mediaextensions {
 
   public final class MediaBrowserExtras {
-    field public static final String KEY_HINT_HOST_PACKAGE_NAME = "androidx.car.app.mediaextensions.KEY_HINT_HOST_PACKAGE_NAME";
-    field public static final String KEY_HINT_VIEW_HEIGHT_PIXELS = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_HEIGHT_PIXELS";
     field public static final String KEY_HINT_VIEW_MAX_CATEGORY_GRID_ITEMS_COUNT_PER_ROW = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_MAX_CATEGORY_GRID_ITEMS_COUNT_PER_ROW";
     field public static final String KEY_HINT_VIEW_MAX_CATEGORY_LIST_ITEMS_COUNT_PER_ROW = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_MAX_CATEGORY_LIST_ITEMS_COUNT_PER_ROW";
     field public static final String KEY_HINT_VIEW_MAX_GRID_ITEMS_COUNT_PER_ROW = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_MAX_GRID_ITEMS_COUNT_PER_ROW";
     field public static final String KEY_HINT_VIEW_MAX_ITEMS_WHILE_RESTRICTED = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_MAX_ITEMS_WHILE_RESTRICTED";
     field public static final String KEY_HINT_VIEW_MAX_LIST_ITEMS_COUNT_PER_ROW = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_MAX_LIST_ITEMS_COUNT_PER_ROW";
-    field public static final String KEY_HINT_VIEW_WIDTH_PIXELS = "androidx.car.app.mediaextensions.KEY_HINT_VIEW_WIDTH_PIXELS";
     field public static final String KEY_ROOT_HINT_MAX_QUEUE_ITEMS_WHILE_RESTRICTED = "androidx.car.app.mediaextensions.KEY_ROOT_HINT_MAX_QUEUE_ITEMS_WHILE_RESTRICTED";
     field public static final String KEY_ROOT_HINT_MEDIA_SESSION_API = "androidx.car.app.mediaextensions.KEY_ROOT_HINT_MEDIA_SESSION_API";
   }
diff --git a/car/app/app/build.gradle b/car/app/app/build.gradle
index 4582736..93d8396 100644
--- a/car/app/app/build.gradle
+++ b/car/app/app/build.gradle
@@ -61,7 +61,7 @@
     implementation ("androidx.media:media:1.6.0")
     // Session and Screen both implement LifeCycleOwner so this needs to be exposed.
     api("androidx.lifecycle:lifecycle-common-java8:2.2.0")
-    api(project(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
 
     annotationProcessor(libs.nullaway)
 
diff --git a/car/app/app/src/main/java/androidx/car/app/mediaextensions/MediaBrowserExtras.java b/car/app/app/src/main/java/androidx/car/app/mediaextensions/MediaBrowserExtras.java
index 7ed0354..473f447 100644
--- a/car/app/app/src/main/java/androidx/car/app/mediaextensions/MediaBrowserExtras.java
+++ b/car/app/app/src/main/java/androidx/car/app/mediaextensions/MediaBrowserExtras.java
@@ -61,44 +61,6 @@
      * {@link androidx.media.MediaBrowserServiceCompat#onLoadChildren(String,
      * MediaBrowserServiceCompat.Result, Bundle)} or to
      * {@link androidx.media.MediaBrowserServiceCompat#onSearch(String, Bundle,
-     * MediaBrowserServiceCompat.Result)} to indicate the package name reported by the caller.
-     *
-     * <p>TYPE: String - the <b>unverified</b> caller's package name
-     */
-    public static final String KEY_HINT_HOST_PACKAGE_NAME =
-            "androidx.car.app.mediaextensions.KEY_HINT_HOST_PACKAGE_NAME";
-
-    /**
-     * {@link Bundle} key used in the options bundle passed to
-     * {@link androidx.media.MediaBrowserServiceCompat#onLoadChildren(String,
-     * MediaBrowserServiceCompat.Result, Bundle)} or to
-     * {@link androidx.media.MediaBrowserServiceCompat#onSearch(String, Bundle,
-     * MediaBrowserServiceCompat.Result)} to indicate the width of the view that will show the
-     * returned media items.
-     *
-     * <p>TYPE: int - width of the view in pixels
-     */
-    public static final String KEY_HINT_VIEW_WIDTH_PIXELS =
-            "androidx.car.app.mediaextensions.KEY_HINT_VIEW_WIDTH_PIXELS";
-
-    /**
-     * {@link Bundle} key used in the options bundle passed to
-     * {@link androidx.media.MediaBrowserServiceCompat#onLoadChildren(String,
-     * MediaBrowserServiceCompat.Result, Bundle)} or to
-     * {@link androidx.media.MediaBrowserServiceCompat#onSearch(String, Bundle,
-     * MediaBrowserServiceCompat.Result)} to indicate the height of the view that will show the
-     * returned media items.
-     *
-     * <p>TYPE: int - height of the view in pixels
-     */
-    public static final String KEY_HINT_VIEW_HEIGHT_PIXELS =
-            "androidx.car.app.mediaextensions.KEY_HINT_VIEW_HEIGHT_PIXELS";
-
-    /**
-     * {@link Bundle} key used in the options bundle passed to
-     * {@link androidx.media.MediaBrowserServiceCompat#onLoadChildren(String,
-     * MediaBrowserServiceCompat.Result, Bundle)} or to
-     * {@link androidx.media.MediaBrowserServiceCompat#onSearch(String, Bundle,
      * MediaBrowserServiceCompat.Result)} to indicate the maximum number of returned items
      * reachable under driving restrictions.
      *
diff --git a/compose/animation/animation-core-lint/src/main/java/androidx/compose/animation/core/lint/AnimationCoreIssueRegistry.kt b/compose/animation/animation-core-lint/src/main/java/androidx/compose/animation/core/lint/AnimationCoreIssueRegistry.kt
index 7dce8cf..a73e703 100644
--- a/compose/animation/animation-core-lint/src/main/java/androidx/compose/animation/core/lint/AnimationCoreIssueRegistry.kt
+++ b/compose/animation/animation-core-lint/src/main/java/androidx/compose/animation/core/lint/AnimationCoreIssueRegistry.kt
@@ -29,7 +29,8 @@
     override val minApi = CURRENT_API
     override val issues get() = listOf(
         TransitionDetector.UnusedTransitionTargetStateParameter,
-        UnrememberedAnimatableDetector.UnrememberedAnimatable
+        UnrememberedAnimatableDetector.UnrememberedAnimatable,
+        ArcAnimationSpecTypeDetector.ArcAnimationSpecTypeIssue
     )
     override val vendor = Vendor(
         vendorName = "Jetpack Compose",
diff --git a/compose/animation/animation-core-lint/src/main/java/androidx/compose/animation/core/lint/ArcAnimationSpecTypeDetector.kt b/compose/animation/animation-core-lint/src/main/java/androidx/compose/animation/core/lint/ArcAnimationSpecTypeDetector.kt
new file mode 100644
index 0000000..cfdaf85
--- /dev/null
+++ b/compose/animation/animation-core-lint/src/main/java/androidx/compose/animation/core/lint/ArcAnimationSpecTypeDetector.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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.compose.animation.core.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.android.tools.lint.detector.api.UastLintUtils.Companion.tryResolveUDeclaration
+import java.util.EnumSet
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UClass
+private const val ANIMATION_CORE_PACKAGE = "androidx.compose.animation.core"
+private const val GEOMETRY_PACKAGE = "androidx.compose.ui.geometry"
+private const val UNIT_PACKAGE = "androidx.compose.ui.unit"
+private const val ARC_ANIMATION_SPEC_NAME = "ArcAnimationSpec"
+private const val ARC_KEYFRAMES_SPEC_NAME = "keyframesWithArcs"
+private const val OFFSET_NAME = "Offset"
+private const val INT_OFFSET_NAME = "IntOffset"
+private const val DP_OFFSET_NAME = "DpOffset"
+private const val ARC_SPEC_FQ_NAME =
+    "$ANIMATION_CORE_PACKAGE.$ARC_ANIMATION_SPEC_NAME"
+private const val OFFSET_FQ_NAME =
+    "$GEOMETRY_PACKAGE.$OFFSET_NAME"
+private const val INT_OFFSET_FQ_NAME =
+    "$UNIT_PACKAGE.$INT_OFFSET_NAME"
+private const val DP_OFFSET_FQ_NAME =
+    "$UNIT_PACKAGE.$DP_OFFSET_NAME"
+private val preferredArcAnimationTypes by lazy(LazyThreadSafetyMode.NONE) {
+    setOf(
+        OFFSET_FQ_NAME,
+        INT_OFFSET_FQ_NAME,
+        DP_OFFSET_FQ_NAME
+    )
+}
+/**
+ * Lint to inform of the expected usage for `ArcAnimationSpec` (and its derivative)
+ * `keyframesWithArcs`.
+ */
+class ArcAnimationSpecTypeDetector : Detector(), SourceCodeScanner {
+    override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)
+    override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
+        override fun visitCallExpression(node: UCallExpression) {
+            when (node.classReference?.resolvedName) {
+                ARC_ANIMATION_SPEC_NAME -> detectTypeParameterInArcAnimation(node)
+            }
+        }
+        private fun detectTypeParameterInArcAnimation(node: UCallExpression) {
+            val typeArg = node.typeArguments.firstOrNull() ?: return
+            val qualifiedTypeName = typeArg.canonicalText
+            // Check that the given type to the call is one of: Offset, IntOffset, DpOffset
+            if (preferredArcAnimationTypes.contains(qualifiedTypeName)) {
+                return
+            }
+            // Node class resolution might be slower, do last
+            val fqClassName =
+                (node.classReference?.tryResolveUDeclaration() as? UClass)?.qualifiedName
+            // Verify that the method calls are from the expected animation classes, otherwise, skip
+            // check
+            if (fqClassName != ARC_SPEC_FQ_NAME) {
+                return
+            }
+            // Generate Lint
+            context.report(
+                issue = ArcAnimationSpecTypeIssue,
+                scope = node,
+                location = context.getNameLocation(node),
+                message = "Arc animation is intended for 2D values such as Offset, IntOffset or " +
+                    "DpOffset.\nOtherwise, the animation might not be what you expect."
+            )
+        }
+    }
+    companion object {
+        val ArcAnimationSpecTypeIssue = Issue.create(
+            id = "ArcAnimationSpecTypeIssue",
+            briefDescription = "$ARC_ANIMATION_SPEC_NAME is " +
+                "designed for 2D values. Particularly, for positional values such as Offset.",
+            explanation = "$ARC_ANIMATION_SPEC_NAME is designed for" +
+                " 2D values. Particularly, for positional values such as Offset.\nTrying to use " +
+                "it for values of different dimensions (Float, Size, Color, etc.) will result " +
+                "in unpredictable animation behavior.",
+            category = Category.CORRECTNESS,
+            priority = 5,
+            severity = Severity.INFORMATIONAL,
+            implementation = Implementation(
+                ArcAnimationSpecTypeDetector::class.java,
+                EnumSet.of(Scope.JAVA_FILE)
+            )
+        )
+    }
+}
diff --git a/compose/animation/animation-core-lint/src/test/java/androidx/compose/animation/core/lint/ArcAnimationSpecTypeDetectorTest.kt b/compose/animation/animation-core-lint/src/test/java/androidx/compose/animation/core/lint/ArcAnimationSpecTypeDetectorTest.kt
new file mode 100644
index 0000000..fda5093
--- /dev/null
+++ b/compose/animation/animation-core-lint/src/test/java/androidx/compose/animation/core/lint/ArcAnimationSpecTypeDetectorTest.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core.lint
+
+import androidx.compose.lint.test.bytecodeStub
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/**
+ * Detector to discourage the use of arc-based animations on types other than the specified (known
+ * 2-dimensional types such as Offset, IntOffset, DpOffset).
+ *
+ * TODO(b/299477780): Support detecting usages on keyframes. Note that it would only apply to usages
+ *   of `KeyframeEntity<T>.using(arcMode: ArcMode)` where arc mode is ArcAbove/ArcBelow.
+ */
+@RunWith(JUnit4::class)
+class ArcAnimationSpecTypeDetectorTest : LintDetectorTest() {
+    override fun getDetector(): Detector = ArcAnimationSpecTypeDetector()
+
+    override fun getIssues(): MutableList<Issue> =
+        mutableListOf(ArcAnimationSpecTypeDetector.ArcAnimationSpecTypeIssue)
+
+    // Simplified version of Arc animation classes in AnimationSpec.kt
+    private val ArcAnimationSpecStub = bytecodeStub(
+        filename = "AnimationSpec.kt",
+        filepath = "androidx/compose/animation/core",
+        checksum = 0x9d0cdf8f,
+        source = """
+            package androidx.compose.animation.core
+
+            class ArcAnimationSpec<T>(val mode: ArcMode, val durationMillis: Int = 400)
+
+            sealed class ArcMode
+
+            object ArcAbove : ArcMode()
+        """,
+        """
+                META-INF/main.kotlin_module:
+                H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijg4uViTsvPF2ILSS0u8S5RYtBiAACf
+                q36HJwAAAA==
+                """,
+        """
+                androidx/compose/animation/core/ArcAbove.class:
+                H4sIAAAAAAAA/41SS2/TQBD+1knzhqbllVDe5ZH0gJuKWyukEECylORAqkio
+                p42zwDb2brXeRD3mxA/hH1QcKoGEIrjxoxCzJoUDl9jyzM4333yzM/LPX1++
+                AXiGxwwNrsZGy/GpH+r4RCfC50rG3EqtCDHCb5uwPdIzkQdjeLICu6fHRM4w
+                5A6kkvY5Q6bRHDK0Gt2JtpFU/vEs9qWywige+S/FOz6NbEerxJppaLXpcTMR
+                Zr85rGAN+RKyKDBk7QeZMOx0V73vPkPhIIzSKzihXAkeLhEY9AeH7X7nVQXr
+                KBcJrDJsd7V57x8LOzJcqoQ0lbapaOL3te1Po4j0Ni4G6AnLx9xywrx4lqFd
+                MmeKzoCBTQg/lS7apdO4RQ0W81LJq3npt5gXfnz0aov5nrfLXuQL3vdPOa/q
+                OeoeQ3OVEd2SqXu1fZEZnIjw6cQybL2ZKitjEaiZTOQoEu1/s9AaO1TIsN6V
+                SvSn8UiYQ04chs2uDnk05Ea6eAmWBnpqQvFauqC+FB7+J4sWbTGbjl53SyV/
+                j6Ic+U3yHr1raXTfbYQi5rI77BzFszT/YMkGCtgmW/nDQIm0HFb5W32D2O4p
+                f4X39hyXP2PjLAU8PEztXTxKf22GK9T06hEyAa4FuB5QaY2OqAe4ia0jsAS3
+                cJvyCcoJ7iTI/QY7flqdFwMAAA==
+                """,
+        """
+                androidx/compose/animation/core/ArcAnimationSpec.class:
+                H4sIAAAAAAAA/5VSS28TVxT+7vg1HgyMXUKCCRQID8cujONCH5gipSCkkeyA
+                cJRNurkZ35obj2eiudcRqyo/odtuWbMACUTVRRV12R9V9Vx7EvLowt3c87jf
+                Oec7j7//+eNPAPfRZmjyqJ/Esv/aC+LRTqyExyM54lrGEXkS4a0mweqBp7cj
+                ggIYQ+3R+sPONt/lXsijgfd8a1sEuv34tIvBPekrIMuQfyQjqR8z3K11ZmDQ
+                jfui7S9vMCx14mTgbQu9lXAZKcJGsZ6AlbcW67VxGFLR7IgCbBQZrg5jHcrI
+                294deTLSIol46PmRTihYBqqAMwxzwSsRDNPoFzzhI0FAhju10w0d8fRMkkF7
+                eaOEszjnoITzDJmasfMoO8ihwrA8c3slFHGhCAtzDOf642QC6cowlIqB+SXM
+                Y8F8X6L29CvjbM2S+9jyaDQ//Y+B+53/mt5T8TMfh/oJjVwn40DHSZcnQ5G0
+                p50XHCJ5laEwENqkYWjUZp8CQ5ninp5on6bq088Bna7QvM81J7Q12s3QLTPz
+                FM0DGtaQ/K+lsZqk9VcY1P5e1bEWLGd/z7Fceow8MG3HsnML+3v1rL2/57KW
+                1bR+nK/k3UzVamYrtm25OdLyf73JW27hZfnQsim8mrVtt0jOCfiz03HPmNIt
+                YrPODCn32CLuDTXD5ZfjSMuR8KNdqeRWKFY/HzMt+clkeOc7MhJr49GWSNY5
+                YRgqnTjg4QZPpLFT582TuQ7v+FjSsz3Ng2GX76RhxZ4cRFyPE9KdXjxOAvFM
+                mo9Lab6NU8ywQvvN0WwtVMzJUm8tsvIkbZIVc6cks2TTMRDqa7J6JM0+5hoV
+                53e49U/4ot74iIv1xY+ovp8ku58myVPoA9KvTQNwGYtmraRNixnNlLDwjdm5
+                ldaFa0KvkGXqtSjYcCxfyf3yGwpl9usP9cbiJ3w5rfUtvRkw57Co4VumktdT
+                vp45I5K5+gdcfHeMH1J+pSkg5Xd0BGXcwFJK5Gii6tsZEmXw3QSVwfcTuYKH
+                JJ8T5iZhbm0i4+O2jzs+algmFXUfDXy1CaZwF/c2UVJYVPAUmgpFhQsK8xO9
+                oHBDYUnhmsL1fwEMDsmpAwYAAA==
+                """,
+        """
+                androidx/compose/animation/core/ArcMode.class:
+                H4sIAAAAAAAA/5VRXWsTQRQ9s9lu0jW229aP1O+KYFOx2xbRh4oQK0IgUbCS
+                lzzIZDPqJLszZXY29DH4U/wHfRJ8kNBHf5R4Z5Pia4Xlfpwz596Zs7///PwF
+                4BkeMjzmami0HJ7Gic5OdC5irmTGrdSKECPilkm6eiiqYAzRiE94nHL1JX4/
+                GInEVlFhCF5KJe0rhsp2s1fHEoIQPqoMvv0qc4Zm55I7Dhn2tztjbVOp4tEk
+                i6Wywiiexm/EZ16k9kir3Joisdp0uRkLc9jshfDcro1HyT/yU1ayDLv/N41h
+                7ULQFZYPueWEedmkQnYxF5ZdAAMbE34qXbdH1XCf4clsuhJ6DS/0otk0pI/q
+                2vPGbHrg7bHX1Zp//j3wIu/8G2MVJzlgbtDOZcxpDfTEuRO1LqjjE5Hsji15
+                fETGMax2pBLvimwgzEc+SAlZ7+iEpz1upOsXYHisC5OIt9I1mx8KZWUmejKX
+                xLaU0rYcnvtbZKrvXkrZc3+UbnqXutg9nfLSzg/Uzkr6HsWgBAPcp1ifH8Ay
+                QiBiVF1ZiJ9S9hbi+llpoxPcmINzQVldxUp59EG54A62KL8gZJW4qI9KG2tt
+                rLexgWtU4nqbZtzsg+VoYLMPP0eY41aOIMftv/zlm/jsAgAA
+                """
+    )
+
+    // Simplified version of Offset.kt in geometry package
+    private val GeometryStub = bytecodeStub(
+        filename = "Offset.kt",
+        filepath = "androidx/compose/ui/geometry",
+        checksum = 0x471b639e,
+        source = """
+            package androidx.compose.ui.geometry
+
+            class Offset
+        """,
+        """
+                META-INF/main.kotlin_module:
+                H4sIAAAAAAAA/2NgYGBmYGBgBGIOBijgMuKST8xLKcrPTKnQS87PLcgvTtVL
+                zMvMTSzJzM8DihSlCvE7wvjBBanJ3iVcvFzMafn5QmwhqcUl3iVKDFoMAHnM
+                zO9bAAAA
+                """,
+        """
+                androidx/compose/ui/geometry/Offset.class:
+                H4sIAAAAAAAA/41RzS5DQRg937S9uIr6r9+NSLBwETsiQSJpUiRIN1bT3sFo
+                74zcmQq7Pos3sJJYSGPpocR3Lw9gc3J+vpk5M/P1/f4BYBdLhBVp4tTq+Clq
+                2eTBOhV1dXSrbKJ8+hyd39w45QdAhMq9fJRRR5rb6Lx5r1rsFgjBvjbaHxAK
+                a+uNMkoIQhQxQCj6O+0Iq/V/7L9HGK+3re9oE50qL2PpJXsieSxwTcpgMAMQ
+                qM3+k87UFrN4m7Dc74WhqIpQVJj1e9V+b0ds0VHp8yUQFZFN7VC2duj3tM22
+                53rHNlaEsbo26qybNFV6JZsddibqtiU7DZnqTP+Z4aXtpi11ojMxd9E1Xieq
+                oZ3m9NAY66XX1jhsQ/Dt/5pmj8FYZRXlGihtvGHwlYnAHGOQm0XMM5Z/BzCE
+                MM8XcpzFYv5RhGHOytco1DBSw2gNY6gwxXgNE5i8BjlMYZpzh9BhxiH4AWXo
+                H/7lAQAA
+                """
+    )
+
+    // Simplified classes of ui/unit package
+    private val UnitStub = bytecodeStub(
+        filename = "Units.kt",
+        filepath = "androidx/compose/ui/unit",
+        checksum = 0x137591fb,
+        source = """
+            package androidx.compose.ui.unit
+
+            class IntOffset
+
+            class DpOffset
+        """,
+        """
+                META-INF/main.kotlin_module:
+                H4sIAAAAAAAA/2NgYGBmYGBgBGIOBijgMuKST8xLKcrPTKnQS87PLcgvTtVL
+                zMvMTSzJzM8DihSlCvE7wvjBBanJ3iVcvFzMafn5QmwhqcUl3iVKDFoMAHnM
+                zO9bAAAA
+                """,
+        """
+                androidx/compose/ui/unit/DpOffset.class:
+                H4sIAAAAAAAA/4VRy0oDMRQ9N7VjHavWd32CuFEXjoo7RfCBUKgKPrpxlXZS
+                jW0TaTList/iH7gSXEhx6UeJd0b3bg7ncZOcJF/f7x8AdrFEWJEm7lodP0cN
+                23m0TkWJjhKjfXTyeNFsOuUHQYTSg3ySUVuau+ii/qAa7OYIwb7myQNCbm29
+                VkQeQYgBDBIG/L12hNXqv7vvEcarLevb2kRnystYesme6DzluCKlUEgBBGqx
+                /6xTtcUs3iYs93thKMoiFCVm/V6539sRW3SU/3wJREmkUzuUri3c8KFus+W5
+                27GNFWGsqo06Tzp11b2W9TY7E1XbkO2a7OpU/5nhlU26DXWqUzF3mRivO6qm
+                neb00BjrpdfWOGxD8NX/iqYvwVhmFWUayG+8ofDKRGCOMcjMAPOMxd8BDCHM
+                8oUMZ7GY/RFhmLPiLXIVjFQwWsEYSkwxXsEEJm9BDlOY5twhdJhxCH4AObkh
+                xeABAAA=
+                """,
+        """
+                androidx/compose/ui/unit/IntOffset.class:
+                H4sIAAAAAAAA/4VRTS9rQRh+3ml71FHUd3FZiAUWDmJHJEhucpIiwe3Gatoz
+                ZbSdkc4csexv8Q+sJBbS3KUfJd5z2Ns8eT7emXlm5uPz7R3APlYIa9IkfauT
+                p6hlew/WqSjVUWq0j2LjL9ptp/wIiFC9l48y6kpzG10071WL3QIhONQ8ekQo
+                bGw2KighCFHECKHo77QjrNd/3/6AMFXvWN/VJjpTXibSS/ZE77HAJSmDcgYg
+                UIf9J52pHWbJLmF1OAhDUROhqDIbDmrDwZ7YoZPS/+dAVEU2tUfZ2vI/PtVt
+                dzyXO7WJIkzWtVHnaa+p+tey2WVnum5bstuQfZ3pHzO8smm/pf7qTCxepsbr
+                nmpopzk9NsZ66bU1DrsQfPefotlTMNZYRbkGSluvKL8wEVhkDHKziCXGyvcA
+                RhHm+XKOC/iT/xJhjLPKDQoxxmNMxJhElSmmYkxj5gbkMIs5zh1Ch3mH4Au3
+                DmZN4gEAAA==
+                """
+    )
+
+    @Test
+    fun testPreferredTypeIssue() {
+        lint().files(
+            kotlin("""
+package foo
+
+import androidx.compose.animation.core.ArcAnimationSpec
+import androidx.compose.animation.core.ArcAbove
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.DpOffset
+import androidx.compose.ui.unit.IntOffset
+
+fun test() {
+    ArcAnimationSpec<Offset>(ArcAbove)
+    ArcAnimationSpec<IntOffset>(ArcAbove)
+    ArcAnimationSpec<DpOffset>(ArcAbove)
+    ArcAnimationSpec<Float>(ArcAbove)
+    ArcAnimationSpec<String>(ArcAbove)
+}
+            """),
+            ArcAnimationSpecStub,
+            GeometryStub,
+            UnitStub
+        ).run()
+            .expect("""src/foo/test.kt:14: Information: Arc animation is intended for 2D values such as Offset, IntOffset or DpOffset.
+Otherwise, the animation might not be what you expect. [ArcAnimationSpecTypeIssue]
+    ArcAnimationSpec<Float>(ArcAbove)
+    ~~~~~~~~~~~~~~~~
+src/foo/test.kt:15: Information: Arc animation is intended for 2D values such as Offset, IntOffset or DpOffset.
+Otherwise, the animation might not be what you expect. [ArcAnimationSpecTypeIssue]
+    ArcAnimationSpec<String>(ArcAbove)
+    ~~~~~~~~~~~~~~~~
+0 errors, 0 warnings""")
+    }
+}
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index b5753d4..2209198 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -208,6 +208,38 @@
     method public static androidx.compose.animation.core.AnimationVector4D AnimationVector(float v1, float v2, float v3, float v4);
   }
 
+  @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi @androidx.compose.runtime.Immutable public final class ArcAnimationSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
+    ctor public ArcAnimationSpec(optional androidx.compose.animation.core.ArcMode mode, optional int durationMillis, optional int delayMillis, optional androidx.compose.animation.core.Easing easing);
+    method public int getDelayMillis();
+    method public int getDurationMillis();
+    method public androidx.compose.animation.core.Easing getEasing();
+    method public androidx.compose.animation.core.ArcMode getMode();
+    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+    property public final int delayMillis;
+    property public final int durationMillis;
+    property public final androidx.compose.animation.core.Easing easing;
+    property public final androidx.compose.animation.core.ArcMode mode;
+  }
+
+  @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi public abstract sealed class ArcMode {
+    field public static final androidx.compose.animation.core.ArcMode.Companion Companion;
+  }
+
+  public static final class ArcMode.Companion {
+  }
+
+  @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi public static final class ArcMode.Companion.ArcAbove extends androidx.compose.animation.core.ArcMode {
+    field public static final androidx.compose.animation.core.ArcMode.Companion.ArcAbove INSTANCE;
+  }
+
+  @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi public static final class ArcMode.Companion.ArcBelow extends androidx.compose.animation.core.ArcMode {
+    field public static final androidx.compose.animation.core.ArcMode.Companion.ArcBelow INSTANCE;
+  }
+
+  @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi public static final class ArcMode.Companion.ArcLinear extends androidx.compose.animation.core.ArcMode {
+    field public static final androidx.compose.animation.core.ArcMode.Companion.ArcLinear INSTANCE;
+  }
+
   @androidx.compose.runtime.Immutable public final class CubicBezierEasing implements androidx.compose.animation.core.Easing {
     ctor public CubicBezierEasing(float a, float b, float c, float d);
     method public float transform(float fraction);
@@ -457,6 +489,7 @@
 
   public static final class KeyframesSpec.KeyframesSpecConfig<T> extends androidx.compose.animation.core.KeyframesSpecBaseConfig<T,androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>> {
     ctor public KeyframesSpec.KeyframesSpecConfig();
+    method @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> using(androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>, androidx.compose.animation.core.ArcMode arcMode);
     method @Deprecated public infix void with(androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>, androidx.compose.animation.core.Easing easing);
   }
 
diff --git a/compose/animation/animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
index 58ef4a3..35903ae 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -208,6 +208,38 @@
     method public static androidx.compose.animation.core.AnimationVector4D AnimationVector(float v1, float v2, float v3, float v4);
   }
 
+  @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi @androidx.compose.runtime.Immutable public final class ArcAnimationSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
+    ctor public ArcAnimationSpec(optional androidx.compose.animation.core.ArcMode mode, optional int durationMillis, optional int delayMillis, optional androidx.compose.animation.core.Easing easing);
+    method public int getDelayMillis();
+    method public int getDurationMillis();
+    method public androidx.compose.animation.core.Easing getEasing();
+    method public androidx.compose.animation.core.ArcMode getMode();
+    method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+    property public final int delayMillis;
+    property public final int durationMillis;
+    property public final androidx.compose.animation.core.Easing easing;
+    property public final androidx.compose.animation.core.ArcMode mode;
+  }
+
+  @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi public abstract sealed class ArcMode {
+    field public static final androidx.compose.animation.core.ArcMode.Companion Companion;
+  }
+
+  public static final class ArcMode.Companion {
+  }
+
+  @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi public static final class ArcMode.Companion.ArcAbove extends androidx.compose.animation.core.ArcMode {
+    field public static final androidx.compose.animation.core.ArcMode.Companion.ArcAbove INSTANCE;
+  }
+
+  @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi public static final class ArcMode.Companion.ArcBelow extends androidx.compose.animation.core.ArcMode {
+    field public static final androidx.compose.animation.core.ArcMode.Companion.ArcBelow INSTANCE;
+  }
+
+  @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi public static final class ArcMode.Companion.ArcLinear extends androidx.compose.animation.core.ArcMode {
+    field public static final androidx.compose.animation.core.ArcMode.Companion.ArcLinear INSTANCE;
+  }
+
   @androidx.compose.runtime.Immutable public final class CubicBezierEasing implements androidx.compose.animation.core.Easing {
     ctor public CubicBezierEasing(float a, float b, float c, float d);
     method public float transform(float fraction);
@@ -457,6 +489,7 @@
 
   public static final class KeyframesSpec.KeyframesSpecConfig<T> extends androidx.compose.animation.core.KeyframesSpecBaseConfig<T,androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>> {
     ctor public KeyframesSpec.KeyframesSpecConfig();
+    method @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> using(androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>, androidx.compose.animation.core.ArcMode arcMode);
     method @Deprecated public infix void with(androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>, androidx.compose.animation.core.Easing easing);
   }
 
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index 4ba3872..c175361 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -44,7 +44,7 @@
                 implementation(project(":compose:ui:ui"))
                 implementation(project(":compose:ui:ui-unit"))
                 implementation(project(":compose:ui:ui-util"))
-                implementation(project(":collection:collection"))
+                implementation("androidx.collection:collection:1.4.0")
                 implementation(libs.kotlinStdlibCommon)
                 api(libs.kotlinCoroutinesCore)
             }
diff --git a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/ArcAnimationSamples.kt b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/ArcAnimationSamples.kt
new file mode 100644
index 0000000..5c8a79f
--- /dev/null
+++ b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/ArcAnimationSamples.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.
+ */
+
+@file:OptIn(ExperimentalAnimationSpecApi::class)
+
+package androidx.compose.animation.core.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.animation.core.ArcAnimationSpec
+import androidx.compose.animation.core.ArcMode.Companion.ArcAbove
+import androidx.compose.animation.core.ExperimentalAnimationSpecApi
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.keyframes
+import androidx.compose.ui.geometry.Offset
+
+@Sampled
+fun OffsetArcAnimationSpec() {
+    // Will interpolate the Offset in arcs such that the curve of the quarter of an Ellipse is above
+    // the center.
+    ArcAnimationSpec<Offset>(mode = ArcAbove)
+}
+
+@Sampled
+fun OffsetKeyframesWithArcsBuilder() {
+    keyframes<Offset> {
+        // Animate for 1.2 seconds
+        durationMillis = 1200
+
+        // Animate to Offset(100f, 100f) at 50% of the animation using LinearEasing then, animate
+        // using ArcAbove for the rest of the animation
+        Offset(100f, 100f) atFraction 0.5f using LinearEasing using ArcAbove
+    }
+}
diff --git a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/AnimationTestUtils.kt b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/AnimationTestUtils.kt
index fcb63dc..41e3cb7 100644
--- a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/AnimationTestUtils.kt
+++ b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/AnimationTestUtils.kt
@@ -119,3 +119,58 @@
     end: Float,
     startVelocity: Float
 ): Float = getVelocityFromNanos(playTimeMillis * MillisToNanos, start, end, startVelocity)
+
+/**
+ * Creates a TwoWayConverter for FloatArray and the given AnimationVector type.
+ */
+internal inline fun <reified V : AnimationVector> createFloatArrayConverter():
+    TwoWayConverter<FloatArray, V> =
+    object : TwoWayConverter<FloatArray, V> {
+        override val convertToVector: (FloatArray) -> V = {
+            when (V::class) {
+                AnimationVector1D::class -> {
+                    AnimationVector(
+                        it.getOrElse(0) { 0f }
+                    )
+                }
+
+                AnimationVector2D::class -> {
+                    AnimationVector(
+                        it.getOrElse(0) { 0f },
+                        it.getOrElse(1) { 0f },
+                    )
+                }
+
+                AnimationVector3D::class -> {
+                    AnimationVector(
+                        it.getOrElse(0) { 0f },
+                        it.getOrElse(1) { 0f },
+                        it.getOrElse(2) { 0f }
+                    )
+                }
+
+                else -> { // 4D
+                    AnimationVector(
+                        it.getOrElse(0) { 0f },
+                        it.getOrElse(1) { 0f },
+                        it.getOrElse(2) { 0f },
+                        it.getOrElse(3) { 0f }
+                    )
+                }
+            } as V
+        }
+        override val convertFromVector: (V) -> FloatArray = { vector ->
+            FloatArray(vector.size, vector::get)
+        }
+    }
+
+/**
+ * Returns an [AnimationVector] of type [V] filled with the given [value].
+ */
+internal inline fun <reified V : AnimationVector> createFilledVector(value: Float): V =
+    when (V::class) {
+        AnimationVector1D::class -> AnimationVector1D(value)
+        AnimationVector2D::class -> AnimationVector2D(value, value)
+        AnimationVector3D::class -> AnimationVector3D(value, value, value)
+        else -> AnimationVector4D(value, value, value, value)
+    } as V
diff --git a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/ArcAnimationTest.kt b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/ArcAnimationTest.kt
new file mode 100644
index 0000000..d9c38ec
--- /dev/null
+++ b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/ArcAnimationTest.kt
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+
+import androidx.compose.animation.core.ArcMode.Companion.ArcAbove
+import androidx.compose.animation.core.ArcMode.Companion.ArcBelow
+import androidx.compose.animation.core.ArcMode.Companion.ArcLinear
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/**
+ * Mostly tests some mathematical assumptions about arcs.
+ */
+@Suppress("JoinDeclarationAndAssignment") // Looks kinda messy
+@OptIn(ExperimentalAnimationSpecApi::class)
+@RunWith(JUnit4::class)
+class ArcAnimationTest {
+    // Animation parameters used in all tests
+    private val timeMillis = 1000
+    private val initialValue = 0f
+    private val targetValue = 300f
+
+    private val error = 0.01f
+
+    @Test
+    fun test2DInterpolation_withArcAbove() {
+        val animation = createArcAnimation<AnimationVector2D>(ArcAbove)
+        var arcValue: AnimationVector2D
+        var linearValue: AnimationVector2D
+
+        // Test values at 25%, 50%, 75%
+        // For arc above Y will always be lower but X will be higher
+        arcValue = animation.valueAt(0.25f)
+        linearValue = linearValueAt(0.25f)
+        assertTrue(arcValue[0] > linearValue[0])
+        assertTrue(arcValue[1] < linearValue[1])
+
+        arcValue = animation.valueAt(0.5f)
+        linearValue = linearValueAt(0.5f)
+        assertTrue(arcValue[0] > linearValue[0])
+        assertTrue(arcValue[1] < linearValue[1])
+
+        arcValue = animation.valueAt(0.75f)
+        linearValue = linearValueAt(0.75f)
+        assertTrue(arcValue[0] > linearValue[0])
+        assertTrue(arcValue[1] < linearValue[1])
+
+        // Test that x at 25% is the complement of y at 75%
+        assertEquals(
+            targetValue - animation.valueAt(0.25f)[0],
+            animation.valueAt(0.75f)[1],
+            error // Bound to have some minor differences :)
+        )
+
+        var arcVelocity: AnimationVector2D
+        // Test that velocity at 50% is equal on both components
+        arcVelocity = animation.velocityAt(0.5f)
+        assertEquals(arcVelocity[0], arcVelocity[1], error)
+
+        // Test that for velocity at 0% only the X component is non-zero
+        arcVelocity = animation.velocityAt(0.0f)
+        assertEquals(0f, arcVelocity[1], error)
+        assertTrue(arcVelocity[0] > error)
+
+        // Test that for velocity at 100% only the X component in non-zero
+        arcVelocity = animation.velocityAt(1f)
+        assertEquals(0f, arcVelocity[0], error)
+        assertTrue(arcVelocity[1] > error)
+    }
+
+    @Test
+    fun test2DInterpolation_withArcBelow() {
+        val animation = createArcAnimation<AnimationVector2D>(ArcBelow)
+        var arcValue: AnimationVector2D
+        var linearValue: AnimationVector2D
+
+        // Test values at 25%, 50%, 75%
+        // For arc below Y will always be higher but X will be lower
+        arcValue = animation.valueAt(0.25f)
+        linearValue = linearValueAt(0.25f)
+        assertTrue(arcValue[0] < linearValue[0])
+        assertTrue(arcValue[1] > linearValue[1])
+
+        arcValue = animation.valueAt(0.5f)
+        linearValue = linearValueAt(0.5f)
+        assertTrue(arcValue[0] < linearValue[0])
+        assertTrue(arcValue[1] > linearValue[1])
+
+        arcValue = animation.valueAt(0.75f)
+        linearValue = linearValueAt(0.75f)
+        assertTrue(arcValue[0] < linearValue[0])
+        assertTrue(arcValue[1] > linearValue[1])
+
+        // Test that Y at 25% is the complement of X at 75%
+        assertEquals(
+            targetValue - animation.valueAt(0.25f)[1],
+            animation.valueAt(0.75f)[0],
+            error // Bound to have some minor differences :)
+        )
+
+        var arcVelocity: AnimationVector2D
+        // Test that velocity at 50% is equal on both components
+        arcVelocity = animation.velocityAt(0.5f)
+        assertEquals(arcVelocity[0], arcVelocity[1], error)
+
+        // Test that for velocity at 0% only the Y component is non-zero
+        arcVelocity = animation.velocityAt(0.0f)
+        assertEquals(0f, arcVelocity[0], error)
+        assertTrue(arcVelocity[1] > error)
+
+        // Test that for velocity at 100% only the Y component in non-zero
+        arcVelocity = animation.velocityAt(1f)
+        assertEquals(0f, arcVelocity[1], error)
+        assertTrue(arcVelocity[0] > error)
+    }
+
+    @Test
+    fun test2DInterpolation_withLinearArc() {
+        val animation = createArcAnimation<AnimationVector2D>(ArcLinear)
+        var arcValue: AnimationVector2D
+        var linearValue: AnimationVector2D
+
+        // Test values at 25%, 50%, 75% should be exactly the same as a linear interpolation
+        arcValue = animation.valueAt(0.25f)
+        linearValue = linearValueAt(0.25f)
+        assertEquals(linearValue, arcValue)
+
+        arcValue = animation.valueAt(0.5f)
+        linearValue = linearValueAt(0.5f)
+        assertEquals(linearValue, arcValue)
+
+        arcValue = animation.valueAt(0.75f)
+        linearValue = linearValueAt(0.75f)
+        assertEquals(linearValue, arcValue)
+
+        var arcVelocity: AnimationVector2D
+        arcVelocity = animation.velocityAt(0.25f)
+        assertEquals(0f, arcVelocity[0] - arcVelocity[1], error)
+
+        arcVelocity = animation.velocityAt(0.5f)
+        assertEquals(0f, arcVelocity[0] - arcVelocity[1], error)
+
+        arcVelocity = animation.velocityAt(0.75f)
+        assertEquals(0f, arcVelocity[0] - arcVelocity[1], error)
+    }
+
+    @Test
+    fun test2DInterpolation_withEasing() {
+        val animation = createArcAnimation<AnimationVector2D>(ArcLinear)
+        val easedAnimation =
+            createArcAnimation<AnimationVector2D>(ArcLinear, FastOutSlowInEasing)
+
+        var arcValue: AnimationVector2D
+        var easedArcValue: AnimationVector2D
+
+        // At 15% of time, the eased animation will lag behind
+        arcValue = animation.valueAt(0.15f)
+        easedArcValue = easedAnimation.valueAt(0.15f)
+        assertTrue(arcValue[0] > easedArcValue[0])
+        assertTrue(arcValue[1] > easedArcValue[1])
+
+        // At 26% of time, both animations will be around the same value
+        arcValue = animation.valueAt(0.26f)
+        easedArcValue = easedAnimation.valueAt(0.26f)
+        // Bigger error here, but still within 1% of the target value
+        assertEquals(arcValue[0], easedArcValue[0], 1f)
+        assertEquals(arcValue[1], easedArcValue[1], 1f)
+
+        // At 50% of time, the eased animation should lead ahead
+        arcValue = animation.valueAt(0.5f)
+        easedArcValue = easedAnimation.valueAt(0.5f)
+        assertTrue(arcValue[0] < easedArcValue[0])
+        assertTrue(arcValue[1] < easedArcValue[1])
+    }
+
+    @Test
+    fun test1DInterpolation_isAlwaysLinear() {
+        // TODO: This behavior might change, to be a forced Arc by repeating the same value on a
+        //  fake second dimension
+        fun testArcMode(arcMode: ArcMode) {
+            val animation = createArcAnimation<AnimationVector1D>(arcMode)
+            var arcValue: AnimationVector1D
+
+            arcValue = animation.valueAt(0.25f)
+            assertEquals(arcValue, linearValueAt<AnimationVector1D>(0.25f))
+
+            arcValue = animation.valueAt(0.5f)
+            assertEquals(arcValue, linearValueAt<AnimationVector1D>(0.5f))
+
+            arcValue = animation.valueAt(0.75f)
+            assertEquals(arcValue, linearValueAt<AnimationVector1D>(0.75f))
+        }
+
+        testArcMode(ArcAbove)
+        testArcMode(ArcBelow)
+        testArcMode(ArcLinear)
+    }
+
+    @Test
+    fun test3DInterpolation_firstPairAsArc() {
+        val animation = createArcAnimation<AnimationVector3D>(ArcAbove)
+        var arcValue: AnimationVector3D
+        var linearValue: AnimationVector3D
+
+        // TODO: Test the 3rd dimension, not as important since we don't have any 3-dimensional
+        //  values out of the box. Currently, this is the same as `test2DInterpolation_withArcAbove`
+
+        // Test values at 25%, 50%, 75%
+        // For arc above Y will always be lower but X will be higher
+        arcValue = animation.valueAt(0.25f)
+        linearValue = linearValueAt(0.25f)
+        assertTrue(arcValue[0] > linearValue[0])
+        assertTrue(arcValue[1] < linearValue[1])
+
+        arcValue = animation.valueAt(0.5f)
+        linearValue = linearValueAt(0.5f)
+        assertTrue(arcValue[0] > linearValue[0])
+        assertTrue(arcValue[1] < linearValue[1])
+
+        arcValue = animation.valueAt(0.75f)
+        linearValue = linearValueAt(0.75f)
+        assertTrue(arcValue[0] > linearValue[0])
+        assertTrue(arcValue[1] < linearValue[1])
+
+        // Test that x at 25% is the complement of y at 75%
+        assertEquals(
+            targetValue - animation.valueAt(0.25f)[0],
+            animation.valueAt(0.75f)[1],
+            error // Bound to have some minor differences :)
+        )
+
+        var arcVelocity: AnimationVector3D
+        // Test that velocity at 50% is equal on both components
+        arcVelocity = animation.velocityAt(0.5f)
+        assertEquals(arcVelocity[0], arcVelocity[1], error)
+
+        // Test that for velocity at 0% only the X component is non-zero
+        arcVelocity = animation.velocityAt(0.0f)
+        assertEquals(0f, arcVelocity[1], error)
+        assertTrue(arcVelocity[0] > error)
+
+        // Test that for velocity at 100% only the Y component in non-zero
+        arcVelocity = animation.velocityAt(1f)
+        assertEquals(0f, arcVelocity[0], error)
+        assertTrue(arcVelocity[1] > error)
+    }
+
+    @Test
+    fun test4DInterpolation_twoPairsAsArcs() {
+        val animation = createArcAnimation<AnimationVector4D>(ArcAbove)
+        var arcValue: AnimationVector4D
+        var linearValue: AnimationVector4D
+
+        // Test values at 25%, 50%, 75%
+        // For arc below Y will always be higher but X will be lower
+        // Similarly for [3] and [2], the second pair
+        arcValue = animation.valueAt(0.25f)
+        linearValue = linearValueAt(0.25f)
+        assertTrue(arcValue[0] > linearValue[0])
+        assertTrue(arcValue[1] < linearValue[1])
+
+        // Second pair
+        assertTrue(arcValue[2] > linearValue[2])
+        assertTrue(arcValue[3] < linearValue[3])
+
+        arcValue = animation.valueAt(0.5f)
+        linearValue = linearValueAt(0.5f)
+        assertTrue(arcValue[0] > linearValue[0])
+        assertTrue(arcValue[1] < linearValue[1])
+
+        // Second pair
+        assertTrue(arcValue[2] > linearValue[2])
+        assertTrue(arcValue[3] < linearValue[3])
+
+        arcValue = animation.valueAt(0.75f)
+        linearValue = linearValueAt(0.75f)
+        assertTrue(arcValue[0] > linearValue[0])
+        assertTrue(arcValue[1] < linearValue[1])
+
+        // Second pair
+        assertTrue(arcValue[2] > linearValue[2])
+        assertTrue(arcValue[3] < linearValue[3])
+    }
+
+    @Test
+    fun testEquals() {
+        // Equal mode with defaults
+        var animationA = ArcAnimationSpec<Float>(ArcAbove)
+        var animationB = ArcAnimationSpec<Float>(ArcAbove)
+        assertEquals(animationA, animationB)
+
+        // Equals with custom values
+        animationA = ArcAnimationSpec(
+            mode = ArcBelow,
+            durationMillis = 13,
+            delayMillis = 17,
+            easing = EaseInOut
+        )
+        animationB = ArcAnimationSpec(
+            mode = ArcBelow,
+            durationMillis = 13,
+            delayMillis = 17,
+            easing = CubicBezierEasing(0.42f, 0.0f, 0.58f, 1.0f) // Re-declared EasInOut
+        )
+        assertEquals(animationA, animationB)
+    }
+
+    @Test
+    fun testNotEquals() {
+        // Different modes
+        var animationA = ArcAnimationSpec<Float>(ArcAbove)
+        var animationB = ArcAnimationSpec<Float>(ArcBelow)
+        assertNotEquals(animationA, animationB)
+
+        // Different duration
+        animationA = ArcAnimationSpec(mode = ArcLinear, durationMillis = 5)
+        animationB = ArcAnimationSpec(mode = ArcLinear, durationMillis = 7)
+        assertNotEquals(animationA, animationB)
+
+        // Different delay
+        animationA = ArcAnimationSpec(mode = ArcLinear, delayMillis = 9)
+        animationB = ArcAnimationSpec(mode = ArcLinear, delayMillis = 11)
+        assertNotEquals(animationA, animationB)
+
+        // Different Easing
+        animationA = ArcAnimationSpec(mode = ArcLinear, easing = EaseInOut)
+        animationB = ArcAnimationSpec(mode = ArcLinear, easing = FastOutSlowInEasing)
+        assertNotEquals(animationA, animationB)
+    }
+
+    private inline fun <reified V : AnimationVector>
+        VectorizedDurationBasedAnimationSpec<V>.valueAt(timePercent: Float): V =
+        this.getValueFromNanos(
+            playTimeNanos = (durationMillis * timePercent).toLong() * 1_000_000,
+            initialValue = createFilledVector(initialValue),
+            targetValue = createFilledVector(targetValue),
+            initialVelocity = createFilledVector(0f)
+        )
+
+    private inline fun <reified V : AnimationVector>
+        VectorizedDurationBasedAnimationSpec<V>.velocityAt(timePercent: Float): V =
+        this.getVelocityFromNanos(
+            playTimeNanos = (durationMillis * timePercent).toLong() * 1_000_000,
+            initialValue = createFilledVector(initialValue),
+            targetValue = createFilledVector(targetValue),
+            initialVelocity = createFilledVector(0f)
+        )
+
+    private inline fun <reified V : AnimationVector> linearValueAt(timePercent: Float): V {
+        val value = timePercent * targetValue
+        return createFilledVector<V>(value)
+    }
+
+    /**
+     * Creates an [ArcAnimationSpec] for the given [AnimationVector] type.
+     */
+    private inline fun <reified V : AnimationVector> createArcAnimation(
+        mode: ArcMode,
+        easing: Easing = LinearEasing
+    ): VectorizedDurationBasedAnimationSpec<V> {
+        val spec = ArcAnimationSpec<FloatArray>(
+            mode = mode,
+            durationMillis = timeMillis,
+            easing = easing
+        )
+        return spec.vectorize(createFloatArrayConverter())
+    }
+}
diff --git a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/KeyframeArcAnimationTest.kt b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/KeyframeArcAnimationTest.kt
new file mode 100644
index 0000000..1976ee8
--- /dev/null
+++ b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/KeyframeArcAnimationTest.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+
+import androidx.compose.animation.core.ArcMode.Companion.ArcAbove
+import androidx.compose.animation.core.ArcMode.Companion.ArcBelow
+import androidx.compose.animation.core.ArcMode.Companion.ArcLinear
+import androidx.compose.ui.geometry.Offset
+import junit.framework.TestCase.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Suppress("JoinDeclarationAndAssignment") // Looks kinda messy
+@OptIn(ExperimentalAnimationSpecApi::class)
+@RunWith(JUnit4::class)
+class KeyframeArcAnimationTest {
+    private val timeMillis = 3000
+    private val initialValue = 0f
+    private val targetValue = 600f
+    private val error = 0.0001f
+
+    @Test
+    fun test2DArcKeyFrame_interpolatedValues() {
+        var arcVector: AnimationVector2D
+        var linearVector: AnimationVector2D
+
+        // Test above, below, linear keyframes
+        val keyframeAnimation = keyframes {
+            durationMillis = timeMillis
+
+            Offset(initialValue, initialValue) at 0 using LinearEasing using ArcAbove
+            Offset(200f, 200f) at 1000 using LinearEasing using ArcBelow
+            Offset(400f, 400f) atFraction 2f / 3f using LinearEasing using ArcLinear
+        }.vectorize(Offset.VectorConverter)
+
+        arcVector = keyframeAnimation.getValueFromNanos(
+            (500).toLong() * 1_000_000,
+            createFilledVector(initialValue),
+            createFilledVector(targetValue),
+            createFilledVector(0f)
+        )
+        linearVector = linearValueAt(1f / 6f)
+        assertTrue(arcVector[0] > linearVector[0]) // X is higher for ArcAbove (in this scenario)
+        assertTrue(arcVector[1] < linearVector[1]) // Y is lower for ArcAbove (in this scenario)
+
+        arcVector = keyframeAnimation.getValueFromNanos(
+            (1500).toLong() * 1_000_000,
+            createFilledVector(initialValue),
+            createFilledVector(targetValue),
+            createFilledVector(0f)
+        )
+        linearVector = linearValueAt(3f / 6f)
+        assertTrue(arcVector[0] < linearVector[0]) // X is lower for ArcBelow
+        assertTrue(arcVector[1] > linearVector[1]) // Y is higher for ArcBelow
+
+        arcVector = keyframeAnimation.getValueFromNanos(
+            (2500).toLong() * 1_000_000,
+            createFilledVector(initialValue),
+            createFilledVector(targetValue),
+            createFilledVector(0f)
+        )
+        linearVector = linearValueAt(5f / 6f)
+        assertEquals(linearVector[0], arcVector[0], error) // X is equals for ArcLinear
+        assertEquals(linearVector[1], arcVector[1], error) // Y is equals for ArcLinear
+    }
+
+    @Test
+    fun test2DArcKeyFrame_multipleEasing() {
+        var arcVector: AnimationVector2D
+        var linearVector: AnimationVector2D
+
+        // We test different Easing curves using Linear arc mode
+        val keyframeAnimation = keyframes {
+            durationMillis = timeMillis
+
+            Offset.Zero at 0 using EaseInCubic using ArcLinear
+            Offset(200f, 200f) at 1000 using LinearEasing using ArcLinear
+            Offset(400f, 400f) atFraction 2f / 3f using EaseOutCubic using ArcLinear
+        }.vectorize(Offset.VectorConverter)
+
+        // Start with EaseInCubic, which is always a lower value
+        arcVector = keyframeAnimation.getValueFromNanos(
+            (500).toLong() * 1_000_000,
+            createFilledVector(initialValue),
+            createFilledVector(targetValue),
+            createFilledVector(0f)
+        )
+        linearVector = linearValueAt(1f / 6f)
+        // X & Y are lower for EaseInCubic
+        assertTrue(arcVector[0] < linearVector[0])
+        assertTrue(arcVector[1] < linearVector[1])
+
+        // Then, LinearEasing, which is always equals
+        arcVector = keyframeAnimation.getValueFromNanos(
+            (1500).toLong() * 1_000_000,
+            createFilledVector(initialValue),
+            createFilledVector(targetValue),
+            createFilledVector(0f)
+        )
+        linearVector = linearValueAt(3f / 6f)
+        assertEquals(linearVector[0], arcVector[0], error) // X is equals with LinearEasing
+        assertEquals(linearVector[1], arcVector[1], error) // Y is equals with LinearEasing
+
+        // Then, EaseOutCubic, which is always a higher value
+        arcVector = keyframeAnimation.getValueFromNanos(
+            (2500).toLong() * 1_000_000,
+            createFilledVector(initialValue),
+            createFilledVector(targetValue),
+            createFilledVector(0f)
+        )
+        linearVector = linearValueAt(5f / 6f)
+        // X & Y are higher for EaseOutCubic
+        assertTrue(arcVector[0] > linearVector[0])
+        assertTrue(arcVector[1] > linearVector[1])
+    }
+
+    private inline fun <reified V : AnimationVector> linearValueAt(timePercent: Float): V {
+        val value = timePercent * targetValue
+        return createFilledVector<V>(value)
+    }
+}
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt
index 46f81d0..003f91b 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt
@@ -176,7 +176,7 @@
 
         for (i in 0 until lowerBoundVector.size) {
             // TODO: is this check too aggressive?
-            check(lowerBoundVector[i] <= upperBoundVector[i]) {
+            checkPrecondition(lowerBoundVector[i] <= upperBoundVector[i]) {
                 "Lower bound must be no greater than upper bound on *all* dimensions. The " +
                     "provided lower bound: $lowerBoundVector is greater than upper bound " +
                     "$upperBoundVector on index $i"
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
index aa8650a..0807442 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
@@ -265,7 +265,7 @@
             ).let {
                 // TODO: Remove after b/232030217
                 for (i in 0 until it.size) {
-                    check(!it.get(i).isNaN()) {
+                    checkPrecondition(!it.get(i).isNaN()) {
                         "AnimationVector cannot contain a NaN. $it. Animation: $this," +
                             " playTimeNanos: $playTimeNanos"
                     }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
index a84bb06..ec76ac5 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
@@ -19,8 +19,12 @@
 import androidx.annotation.IntRange
 import androidx.collection.MutableIntList
 import androidx.collection.MutableIntObjectMap
+import androidx.collection.emptyIntObjectMap
+import androidx.collection.intListOf
 import androidx.collection.mutableIntObjectMapOf
 import androidx.compose.animation.core.AnimationConstants.DefaultDurationMillis
+import androidx.compose.animation.core.ArcMode.Companion.ArcBelow
+import androidx.compose.animation.core.ArcMode.Companion.ArcLinear
 import androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
@@ -168,6 +172,75 @@
 }
 
 /**
+ * [DurationBasedAnimationSpec] that interpolates 2-dimensional values using arcs of quarter of an
+ * Ellipse.
+ *
+ * To interpolate with [keyframes] use [KeyframesSpecConfig.using] with an [ArcMode].
+ *
+ * &nbsp;
+ *
+ * As such, it's recommended that [ArcAnimationSpec] is only used for positional values such as:
+ * [Offset], [IntOffset] or [androidx.compose.ui.unit.DpOffset].
+ *
+ * &nbsp;
+ *
+ * The orientation of the arc is indicated by the given [mode].
+ *
+ * Do note, that if the target value being animated only changes in one dimension, you'll only be
+ * able to get a linear curve.
+ *
+ * Similarly, one-dimensional values will always only interpolate on a linear curve.
+ *
+ * @param mode Orientation of the arc.
+ * @param durationMillis Duration of the animation. [DefaultDurationMillis] by default.
+ * @param delayMillis Time the animation waits before starting. 0 by default.
+ * @param easing [Easing] applied on the animation curve. [FastOutSlowInEasing] by default.
+ *
+ * @see ArcMode
+ * @see keyframes
+ *
+ * @sample androidx.compose.animation.core.samples.OffsetArcAnimationSpec
+ */
+@ExperimentalAnimationSpecApi
+@Immutable
+class ArcAnimationSpec<T>(
+    val mode: ArcMode = ArcBelow,
+    val durationMillis: Int = DefaultDurationMillis,
+    val delayMillis: Int = 0,
+    val easing: Easing = FastOutSlowInEasing // Same default as tween()
+) : DurationBasedAnimationSpec<T> {
+    override fun <V : AnimationVector> vectorize(
+        converter: TwoWayConverter<T, V>
+    ): VectorizedDurationBasedAnimationSpec<V> =
+        VectorizedKeyframesSpec(
+            timestamps = intListOf(0, durationMillis),
+            keyframes = emptyIntObjectMap(),
+            durationMillis = durationMillis,
+            delayMillis = delayMillis,
+            defaultEasing = easing,
+            initialArcMode = mode
+        )
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is ArcAnimationSpec<*>) return false
+
+        if (mode != other.mode) return false
+        if (durationMillis != other.durationMillis) return false
+        if (delayMillis != other.delayMillis) return false
+        return easing == other.easing
+    }
+
+    override fun hashCode(): Int {
+        var result = mode.hashCode()
+        result = 31 * result + durationMillis
+        result = 31 * result + delayMillis
+        result = 31 * result + easing.hashCode()
+        return result
+    }
+}
+
+/**
  * This class defines the two types of [StartOffset]: [StartOffsetType.Delay] and
  * [StartOffsetType.FastForward].
  * [StartOffsetType.Delay] delays the start of the animation, whereas [StartOffsetType.FastForward]
@@ -487,7 +560,11 @@
  * You can also provide a custom [Easing] for the interval with use of [with] function applied
  * for the interval starting keyframe.
  * @sample androidx.compose.animation.core.samples.KeyframesBuilderWithEasing
-
+ *
+ * Values can be animated using arcs of quarter of an Ellipse with [KeyframesSpecConfig.using] and
+ * [ArcMode]:
+ *
+ * @sample androidx.compose.animation.core.samples.OffsetKeyframesWithArcsBuilder
  */
 @Immutable
 class KeyframesSpec<T>(val config: KeyframesSpecConfig<T>) : DurationBasedAnimationSpec<T> {
@@ -501,6 +578,7 @@
      * @see keyframes
      */
     class KeyframesSpecConfig<T> : KeyframesSpecBaseConfig<T, KeyframeEntity<T>>() {
+        @OptIn(ExperimentalAnimationSpecApi::class)
         override fun createEntityFor(value: T): KeyframeEntity<T> = KeyframeEntity(value)
 
         /**
@@ -520,37 +598,86 @@
         infix fun KeyframeEntity<T>.with(easing: Easing) {
             this.easing = easing
         }
+
+        /**
+         * [ArcMode] applied from this keyframe to the next.
+         *
+         * Note that arc modes are meant for objects with even dimensions (such as [Offset] and its
+         * variants). Where each value pair is animated as an arc. So, if the object has odd
+         * dimensions the last value will always animate linearly.
+         *
+         * &nbsp;
+         *
+         * The order of each value in an object with multiple dimensions is given by the applied
+         * vector converter in [KeyframesSpec.vectorize].
+         *
+         * E.g.: [RectToVector] assigns its values as `[left, top, right, bottom]` so the pairs of
+         * dimensions animated as arcs are: `[left, top]` and `[right, bottom]`.
+         */
+        @ExperimentalAnimationSpecApi
+        infix fun KeyframeEntity<T>.using(arcMode: ArcMode): KeyframeEntity<T> {
+            this.arcMode = arcMode
+            return this
+        }
     }
 
+    @OptIn(ExperimentalAnimationSpecApi::class)
     override fun <V : AnimationVector> vectorize(
         converter: TwoWayConverter<T, V>
     ): VectorizedKeyframesSpec<V> {
-        @SuppressWarnings("PrimitiveInCollection") // Consumed by stable public API
-        val vectorizedKeyframes = mutableMapOf<Int, Pair<V, Easing>>()
+        // Max capacity is +2 to account for when the start/end timestamps are not included
+        val timestamps = MutableIntList(config.keyframes.size + 2)
+        val timeToInfoMap =
+            MutableIntObjectMap<VectorizedKeyframeSpecElementInfo<V>>(config.keyframes.size)
         config.keyframes.forEach { key, value ->
-            vectorizedKeyframes[key] = value.toPair(converter.convertToVector)
+            timestamps.add(key)
+            timeToInfoMap[key] = VectorizedKeyframeSpecElementInfo(
+                vectorValue = converter.convertToVector(value.value),
+                easing = value.easing,
+                arcMode = value.arcMode
+            )
         }
+
+        if (!config.keyframes.contains(0)) {
+            timestamps.add(0, 0)
+        }
+        if (!config.keyframes.contains(config.durationMillis)) {
+            timestamps.add(config.durationMillis)
+        }
+        timestamps.sort()
+
         return VectorizedKeyframesSpec(
-            keyframes = vectorizedKeyframes,
+            timestamps = timestamps,
+            keyframes = timeToInfoMap,
             durationMillis = config.durationMillis,
-            delayMillis = config.delayMillis
+            delayMillis = config.delayMillis,
+            defaultEasing = LinearEasing,
+            initialArcMode = ArcLinear
         )
     }
 
     /**
      * Holder class for building a keyframes animation.
      */
+    @OptIn(ExperimentalAnimationSpecApi::class)
     class KeyframeEntity<T> internal constructor(
         value: T,
-        easing: Easing = LinearEasing
+        easing: Easing = LinearEasing,
+        internal var arcMode: ArcMode = ArcMode.Companion.ArcLinear
     ) : KeyframeBaseEntity<T>(value = value, easing = easing) {
 
         override fun equals(other: Any?): Boolean {
-            return other is KeyframeEntity<*> && other.value == value && other.easing == easing
+            if (other === this) return true
+            if (other !is KeyframeEntity<*>) return false
+
+            return other.value == value && other.easing == easing && other.arcMode == arcMode
         }
 
         override fun hashCode(): Int {
-            return value.hashCode() * 31 + easing.hashCode()
+            var result = value?.hashCode() ?: 0
+            result = 31 * result + arcMode.hashCode()
+            result = 31 * result + easing.hashCode()
+            return result
         }
     }
 }
@@ -645,6 +772,11 @@
  *
  * @sample androidx.compose.animation.core.samples.KeyframesBuilderWithEasing
  *
+ * Values can be animated using arcs of quarter of an Ellipse with [KeyframesSpecConfig.using] and
+ * [ArcMode]:
+ *
+ * @sample androidx.compose.animation.core.samples.OffsetKeyframesWithArcsBuilder
+ *
  * @param init Initialization function for the [KeyframesSpec] animation
  * @see KeyframesSpec.KeyframesSpecConfig
  */
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ArcSpline.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ArcSpline.kt
new file mode 100644
index 0000000..1ede230
--- /dev/null
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ArcSpline.kt
@@ -0,0 +1,382 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+
+import kotlin.math.abs
+import kotlin.math.cos
+import kotlin.math.hypot
+import kotlin.math.sin
+
+/**
+ * This provides a curve fit system that stitches the x,y path together with
+ * quarter ellipses.
+ *
+ * @param arcModes Array of arc mode values. Expected to be of size n - 1.
+ * @param timePoints Array of timestamps. Expected to be of size n. Seconds preferred.
+ * @param y Array of values (of size n), where each value is spread on a [FloatArray] for each of
+ * its dimensions, expected to be of even size since two values are needed to interpolate arcs.
+ */
+@ExperimentalAnimationSpecApi
+internal class ArcSpline(
+    arcModes: IntArray,
+    timePoints: FloatArray,
+    y: Array<FloatArray>
+) {
+    private val arcs: Array<Array<Arc>>
+    private val isExtrapolate = true
+
+    init {
+        var mode = StartVertical
+        var last = StartVertical
+
+        arcs = Array(timePoints.size - 1) { i ->
+            when (arcModes[i]) {
+                ArcStartVertical -> {
+                    mode = StartVertical
+                    last = mode
+                }
+
+                ArcStartHorizontal -> {
+                    mode = StartHorizontal
+                    last = mode
+                }
+
+                ArcStartFlip -> {
+                    mode = if (last == StartVertical) StartHorizontal else StartVertical
+                    last = mode
+                }
+
+                ArcStartLinear -> mode = StartLinear
+                ArcAbove -> mode = UpArc
+                ArcBelow -> mode = DownArc
+            }
+            val dim = y[i].size / 2 + y[i].size % 2
+            Array(dim) { j ->
+                val k = j * 2
+                Arc(
+                    mode = mode,
+                    time1 = timePoints[i],
+                    time2 = timePoints[i + 1],
+                    x1 = y[i][k],
+                    y1 = y[i][k + 1],
+                    x2 = y[i + 1][k],
+                    y2 = y[i + 1][k + 1]
+                )
+            }
+        }
+    }
+
+    /**
+     * get the values of the at t point in time.
+     */
+    fun getPos(time: Float, v: FloatArray) {
+        var t = time
+        if (isExtrapolate) {
+            if (t < arcs[0][0].time1 || t > arcs[arcs.size - 1][0].time2) {
+                val p: Int
+                val t0: Float
+                if (t > arcs[arcs.size - 1][0].time2) {
+                    p = arcs.size - 1
+                    t0 = arcs[arcs.size - 1][0].time2
+                } else {
+                    p = 0
+                    t0 = arcs[0][0].time1
+                }
+                val dt = t - t0
+
+                var i = 0
+                var j = 0
+                while (i < v.size) {
+                    if (arcs[p][j].isLinear) {
+                        v[i] = arcs[p][j].getLinearX(t0) + dt * arcs[p][j].getLinearDX()
+                        v[i + 1] = arcs[p][j].getLinearY(t0) + dt * arcs[p][j].getLinearDY()
+                    } else {
+                        arcs[p][j].setPoint(t0)
+                        v[i] = arcs[p][j].calcX() + dt * arcs[p][j].calcDX()
+                        v[i + 1] = arcs[p][j].calcY() + dt * arcs[p][j].calcDY()
+                    }
+                    i += 2
+                    j++
+                }
+                return
+            }
+        } else {
+            if (t < arcs[0][0].time1) {
+                t = arcs[0][0].time1
+            }
+            if (t > arcs[arcs.size - 1][0].time2) {
+                t = arcs[arcs.size - 1][0].time2
+            }
+        }
+
+        // TODO: Consider passing the index from the caller to improve performance
+        var populated = false
+        for (i in arcs.indices) {
+            var k = 0
+            var j = 0
+            while (j < v.size) {
+                if (t <= arcs[i][k].time2) {
+                    if (arcs[i][k].isLinear) {
+                        v[j] = arcs[i][k].getLinearX(t)
+                        v[j + 1] = arcs[i][k].getLinearY(t)
+                        populated = true
+                    } else {
+                        arcs[i][k].setPoint(t)
+                        v[j] = arcs[i][k].calcX()
+                        v[j + 1] = arcs[i][k].calcY()
+                        populated = true
+                    }
+                }
+                j += 2
+                k++
+            }
+            if (populated) {
+                return
+            }
+        }
+    }
+
+    /**
+     * Get the differential which of the curves at point t
+     */
+    fun getSlope(time: Float, v: FloatArray) {
+        var t = time
+        if (t < arcs[0][0].time1) {
+            t = arcs[0][0].time1
+        } else if (t > arcs[arcs.size - 1][0].time2) {
+            t = arcs[arcs.size - 1][0].time2
+        }
+        var populated = false
+        // TODO: Consider passing the index from the caller to improve performance
+        for (i in arcs.indices) {
+            var j = 0
+            var k = 0
+            while (j < v.size) {
+                if (t <= arcs[i][k].time2) {
+                    if (arcs[i][k].isLinear) {
+                        v[j] = arcs[i][k].getLinearDX()
+                        v[j + 1] = arcs[i][k].getLinearDY()
+                        populated = true
+                    } else {
+                        arcs[i][k].setPoint(t)
+                        v[j] = arcs[i][k].calcDX()
+                        v[j + 1] = arcs[i][k].calcDY()
+                        populated = true
+                    }
+                }
+                j += 2
+                k++
+            }
+            if (populated) {
+                return
+            }
+        }
+    }
+
+    class Arc internal constructor(
+        mode: Int,
+        val time1: Float,
+        val time2: Float,
+        private val x1: Float,
+        private val y1: Float,
+        private val x2: Float,
+        private val y2: Float
+    ) {
+        private var arcDistance = 0f
+        private var tmpSinAngle = 0f
+        private var tmpCosAngle = 0f
+
+        private val lut: FloatArray
+        private val oneOverDeltaTime: Float
+        private val ellipseA: Float
+        private val ellipseB: Float
+        private val ellipseCenterX: Float // also used to cache the slope in the unused center
+        private val ellipseCenterY: Float // also used to cache the slope in the unused center
+        private val arcVelocity: Float
+        private val isVertical: Boolean
+
+        val isLinear: Boolean
+
+        init {
+            val dx = x2 - x1
+            val dy = y2 - y1
+            isVertical = when (mode) {
+                StartVertical -> true
+                UpArc -> dy < 0
+                DownArc -> dy > 0
+                else -> false
+            }
+            oneOverDeltaTime = 1 / (this.time2 - this.time1)
+
+            var isLinear = false
+            if (StartLinear == mode) {
+                isLinear = true
+            }
+            if (isLinear || abs(dx) < Epsilon || abs(dy) < Epsilon) {
+                isLinear = true
+                arcDistance = hypot(dy, dx)
+                arcVelocity = arcDistance * oneOverDeltaTime
+                ellipseCenterX =
+                    dx / (this.time2 - this.time1) // cache the slope in the unused center
+                ellipseCenterY =
+                    dy / (this.time2 - this.time1) // cache the slope in the unused center
+                lut = FloatArray(101)
+                ellipseA = Float.NaN
+                ellipseB = Float.NaN
+            } else {
+                lut = FloatArray(101)
+                ellipseA = dx * if (isVertical) -1 else 1
+                ellipseB = dy * if (isVertical) 1 else -1
+                ellipseCenterX = if (isVertical) x2 else x1
+                ellipseCenterY = if (isVertical) y1 else y2
+                buildTable(x1, y1, x2, y2)
+                arcVelocity = arcDistance * oneOverDeltaTime
+            }
+            this.isLinear = isLinear
+        }
+
+        fun setPoint(time: Float) {
+            val percent = (if (isVertical) time2 - time else time - time1) * oneOverDeltaTime
+            val angle = Math.PI.toFloat() * 0.5f * lookup(percent)
+            tmpSinAngle = sin(angle)
+            tmpCosAngle = cos(angle)
+        }
+
+        fun calcX(): Float {
+            return ellipseCenterX + ellipseA * tmpSinAngle
+        }
+
+        fun calcY(): Float {
+            return ellipseCenterY + ellipseB * tmpCosAngle
+        }
+
+        fun calcDX(): Float {
+            val vx = ellipseA * tmpCosAngle
+            val vy = -ellipseB * tmpSinAngle
+            val norm = arcVelocity / hypot(vx, vy)
+            return if (isVertical) -vx * norm else vx * norm
+        }
+
+        fun calcDY(): Float {
+            val vx = ellipseA * tmpCosAngle
+            val vy = -ellipseB * tmpSinAngle
+            val norm = arcVelocity / hypot(vx, vy)
+            return if (isVertical) -vy * norm else vy * norm
+        }
+
+        fun getLinearX(time: Float): Float {
+            var t = time
+            t = (t - time1) * oneOverDeltaTime
+            return x1 + t * (x2 - x1)
+        }
+
+        fun getLinearY(time: Float): Float {
+            var t = time
+            t = (t - time1) * oneOverDeltaTime
+            return y1 + t * (y2 - y1)
+        }
+
+        fun getLinearDX(): Float {
+            return ellipseCenterX
+        }
+
+        fun getLinearDY(): Float {
+            return ellipseCenterY
+        }
+
+        private fun lookup(v: Float): Float {
+            if (v <= 0) {
+                return 0.0f
+            }
+            if (v >= 1) {
+                return 1.0f
+            }
+            val pos = v * (lut.size - 1)
+            val iv = pos.toInt()
+            val off = pos - pos.toInt()
+            return lut[iv] + off * (lut[iv + 1] - lut[iv])
+        }
+
+        private fun buildTable(x1: Float, y1: Float, x2: Float, y2: Float) {
+            val a = x2 - x1
+            val b = y1 - y2
+            var lx = 0f
+            var ly = 0f
+            var dist = 0f
+            for (i in ourPercent.indices) {
+                val angle = Math.toRadians(90.0 * i / (ourPercent.size - 1)).toFloat()
+                val s = sin(angle)
+                val c = cos(angle)
+                val px = a * s
+                val py = b * c
+                if (i > 0) {
+                    dist += hypot((px - lx), (py - ly))
+                    ourPercent[i] = dist
+                }
+                lx = px
+                ly = py
+            }
+            arcDistance = dist
+            for (i in ourPercent.indices) {
+                ourPercent[i] /= dist
+            }
+            for (i in lut.indices) {
+                val pos = i / (lut.size - 1).toFloat()
+                val index = ourPercent.binarySearch(pos)
+                if (index >= 0) {
+                    lut[i] = index / (ourPercent.size - 1).toFloat()
+                } else if (index == -1) {
+                    lut[i] = 0f
+                } else {
+                    val p1 = -index - 2
+                    val p2 = -index - 1
+                    val ans =
+                        (p1 + (pos - ourPercent[p1]) / (ourPercent[p2] - ourPercent[p1])) /
+                            (ourPercent.size - 1)
+                    lut[i] = ans
+                }
+            }
+        }
+
+        companion object {
+            private var _ourPercent: FloatArray? = null
+            private val ourPercent: FloatArray
+                get() {
+                    if (_ourPercent != null) {
+                        return _ourPercent!!
+                    }
+                    _ourPercent = FloatArray(91)
+                    return _ourPercent!!
+                }
+            private const val Epsilon = 0.001f
+        }
+    }
+
+    companion object {
+        const val ArcStartVertical = 1
+        const val ArcStartHorizontal = 2
+        const val ArcStartFlip = 3
+        const val ArcBelow = 4
+        const val ArcAbove = 5
+        const val ArcStartLinear = 0
+        private const val StartVertical = 1
+        private const val StartHorizontal = 2
+        private const val StartLinear = 3
+        private const val DownArc = 4
+        private const val UpArc = 5
+    }
+}
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
index fbcabe4..c62aaa6 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
@@ -106,7 +106,7 @@
     private val d: Float
 ) : Easing {
     init {
-        require(!a.isNaN() && !b.isNaN() && !c.isNaN() && !d.isNaN()) {
+        requirePrecondition(!a.isNaN() && !b.isNaN() && !c.isNaN() && !d.isNaN()) {
             "Parameters to CubicBezierEasing cannot be NaN. Actual parameters are: $a, $b, $c, $d."
         }
     }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/IntListExtension.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/IntListExtension.kt
index 8f3214a..65476cc 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/IntListExtension.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/IntListExtension.kt
@@ -46,7 +46,7 @@
  */
 @JvmOverloads
 internal fun IntList.binarySearch(element: Int, fromIndex: Int = 0, toIndex: Int = size): Int {
-    require(fromIndex <= toIndex) { "fromIndex($fromIndex) > toIndex($toIndex)" }
+    requirePrecondition(fromIndex <= toIndex) { "fromIndex($fromIndex) > toIndex($toIndex)" }
     if (fromIndex < 0) {
         throw IndexOutOfBoundsException("Index out of range: $fromIndex")
     }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
index 45e1cd0..81795d2 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
@@ -68,7 +68,7 @@
             // the transform() function.
             val segmentIntervals = IntervalTree<PathSegment>().apply {
                 for (segment in path) {
-                    require(segment.type != PathSegment.Type.Close) {
+                    requirePrecondition(segment.type != PathSegment.Type.Close) {
                         "The path cannot contain a close() command."
                     }
                     if (segment.type != PathSegment.Type.Move &&
@@ -80,24 +80,20 @@
                 }
             }
 
-            require(0.0f in segmentIntervals) {
-                "The easing path must start at 0.0f."
-            }
-
-            require(1.0f in segmentIntervals) {
-                "The easing path must end at 1.0f."
+            requirePrecondition(0.0f in segmentIntervals && 1.0f in segmentIntervals) {
+                "The easing path must start at 0.0f and end at 1.0f."
             }
 
             intervals = segmentIntervals
         }
 
         val result = intervals.findFirstOverlap(fraction)
-        val segment = checkNotNull(result.data) {
+        val segment = checkPreconditionNotNull(result.data) {
             "The easing path is invalid. Make sure it is continuous on the x axis."
         }
 
         val t = findFirstRoot(segment, fraction)
-        check(!t.isNaN()) {
+        checkPrecondition(!t.isNaN()) {
             "The easing path is invalid. Make sure it does not contain NaN/Infinity values."
         }
 
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Preconditions.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Preconditions.kt
new file mode 100644
index 0000000..2b38bbc
--- /dev/null
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Preconditions.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.contract
+
+// This function exists so we do *not* inline the throw. It keeps
+// the call site much smaller and since it's the slow path anyway,
+// we don't mind the extra function call
+internal fun throwIllegalArgumentException(message: String): Nothing {
+    throw IllegalArgumentException(message)
+}
+
+// Like Kotlin's require() but without the .toString() call
+@Suppress("BanInlineOptIn") // same opt-in as using Kotlin's require()
+@OptIn(ExperimentalContracts::class)
+internal inline fun requirePrecondition(value: Boolean, lazyMessage: () -> String) {
+    contract {
+        returns() implies value
+    }
+    if (!value) {
+        throwIllegalArgumentException(lazyMessage())
+    }
+}
+
+// See above
+internal fun throwIllegalStateException(message: String): Nothing {
+    throw IllegalStateException(message)
+}
+
+// Like Kotlin's check() but without the .toString() call
+@Suppress("BanInlineOptIn") // same opt-in as using Kotlin's check()
+@OptIn(ExperimentalContracts::class)
+internal inline fun checkPrecondition(value: Boolean, lazyMessage: () -> String) {
+    contract {
+        returns() implies value
+    }
+    if (!value) {
+        throwIllegalStateException(lazyMessage())
+    }
+}
+
+// Like Kotlin's checkNotNull() but without the .toString() call
+@Suppress("BanInlineOptIn") // same opt-in as using Kotlin's check()
+@OptIn(ExperimentalContracts::class)
+internal inline fun <T : Any> checkPreconditionNotNull(value: T?, lazyMessage: () -> String): T {
+    contract {
+        returns() implies (value != null)
+    }
+
+    if (value == null) {
+        throwIllegalStateException(lazyMessage())
+    }
+    return value
+}
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
index 8316bd1..56ae35c 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
@@ -309,7 +309,7 @@
 internal val CoroutineContext.durationScale: Float
     get() {
         val scale = this[MotionDurationScale]?.scaleFactor ?: 1f
-        check(scale >= 0f) { "negative scale factor" }
+        checkPrecondition(scale >= 0f) { "negative scale factor" }
         return scale
     }
 
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
index 3fc38f6..f9310fc 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
@@ -267,7 +267,7 @@
         targetState: S = this.targetState,
         @FloatRange(from = 0.0, to = 1.0) fraction: Float = 0f
     ) {
-        require(fraction in 0f..1f) {
+        requirePrecondition(fraction in 0f..1f) {
             "Expecting fraction between 0 and 1. Got $fraction"
         }
         val transition = transition ?: return
@@ -425,7 +425,7 @@
     }
 
     override fun transitionConfigured(transition: Transition<S>) {
-        check(this.transition == null || transition == this.transition) {
+        checkPrecondition(this.transition == null || transition == this.transition) {
             "An instance of SeekableTransitionState has been used in different Transitions. " +
                 "Previous instance: ${this.transition}, new instance: $transition"
         }
@@ -996,7 +996,7 @@
             val playTime =
                 if (durationScale > 0f) {
                     val scaledTime = (playTimeNanos - offsetTimeNanos) / durationScale
-                    check(!scaledTime.isNaN()) {
+                    checkPrecondition(!scaledTime.isNaN()) {
                         "Duration scale adjusted time is NaN. Duration scale: $durationScale," +
                             "playTimeNanos: $playTimeNanos, offsetTimeNanos: $offsetTimeNanos"
                     }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
index 3fd4de1..ddcaa2c 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
@@ -16,8 +16,11 @@
 
 package androidx.compose.animation.core
 
+import androidx.collection.IntList
+import androidx.collection.IntObjectMap
+import androidx.collection.MutableIntList
+import androidx.collection.MutableIntObjectMap
 import androidx.compose.animation.core.AnimationConstants.DefaultDurationMillis
-import androidx.compose.animation.core.internal.JvmDefaultWithCompatibility
 import kotlin.math.min
 
 /**
@@ -214,24 +217,162 @@
  *          delayMillis = delay
  *     )
  *
- * @param keyframes a map from time to a value/easing function pair. The value in each entry
- *                  defines the animation value at that time, and the easing curve is used in the
- *                  interval starting from that time.
- * @param durationMillis total duration of the animation
- * @param delayMillis the amount of the time the animation should wait before it starts. Defaults to
- *                    0.
+ * The interpolation between each value is dictated by [VectorizedKeyframeSpecElementInfo.arcMode] on each
+ * keyframe. If no keyframe information is provided, [initialArcMode] is used.
  *
  * @see [KeyframesSpec]
  */
-class VectorizedKeyframesSpec<V : AnimationVector>(
-    private val keyframes: Map<Int, Pair<V, Easing>>,
+@OptIn(ExperimentalAnimationSpecApi::class)
+class VectorizedKeyframesSpec<V : AnimationVector> internal constructor(
+    // List of all timestamps. Must include start (time = 0), end (time = durationMillis) and all
+    // other timestamps found in [keyframes].
+    private val timestamps: IntList,
+    private val keyframes: IntObjectMap<VectorizedKeyframeSpecElementInfo<V>>,
     override val durationMillis: Int,
-    override val delayMillis: Int = 0
+    override val delayMillis: Int,
+    // Easing used for any segment of time not covered by [keyframes].
+    private val defaultEasing: Easing,
+    // The [ArcMode] used from time `0` until the first keyframe. So, it applies
+    // for the entire duration if [keyframes] is empty.
+    private val initialArcMode: ArcMode
 ) : VectorizedDurationBasedAnimationSpec<V> {
+    /**
+     * @param keyframes a map from time to a value/easing function pair. The value in each entry
+     *                  defines the animation value at that time, and the easing curve is used in
+     *                  the interval starting from that time.
+     * @param durationMillis total duration of the animation
+     * @param delayMillis the amount of the time the animation should wait before it starts.
+     *                    Defaults to 0.
+     */
+    constructor(
+        keyframes: Map<Int, Pair<V, Easing>>,
+        durationMillis: Int,
+        delayMillis: Int = 0
+    ) : this(
+        timestamps = kotlin.run {
+            val times = MutableIntList(keyframes.size + 2)
+            keyframes.forEach { (t, _) ->
+                times.add(t)
+            }
+            if (!keyframes.containsKey(0)) {
+                times.add(0, 0)
+            }
+            if (!keyframes.containsKey(durationMillis)) {
+                times.add(durationMillis)
+            }
+            times.sort()
+            return@run times
+        },
+        keyframes = kotlin.run {
+            val timeToInfoMap = MutableIntObjectMap<VectorizedKeyframeSpecElementInfo<V>>()
+            keyframes.forEach { (time, valueEasing) ->
+                timeToInfoMap[time] = VectorizedKeyframeSpecElementInfo(
+                    vectorValue = valueEasing.first,
+                    easing = valueEasing.second,
+                    arcMode = ArcMode.Companion.ArcLinear
+                )
+            }
 
+            return@run timeToInfoMap
+        },
+        durationMillis = durationMillis,
+        delayMillis = delayMillis,
+        defaultEasing = LinearEasing,
+        initialArcMode = ArcMode.Companion.ArcLinear
+    )
+
+    /**
+     * List of time range for the given keyframes.
+     *
+     * This will be used to do a faster lookup for the corresponding Easing curves.
+     */
+    private lateinit var modes: IntArray
+    private lateinit var times: FloatArray
     private lateinit var valueVector: V
     private lateinit var velocityVector: V
 
+    // Objects for ArcSpline
+    private lateinit var lastInitialValue: V
+    private lateinit var lastTargetValue: V
+    private lateinit var posArray: FloatArray
+    private lateinit var slopeArray: FloatArray
+    private lateinit var arcSpline: ArcSpline
+
+    private fun init(initialValue: V, targetValue: V, initialVelocity: V) {
+        var requiresArcSpline = ::arcSpline.isInitialized
+
+        // Only need to initialize once
+        if (!::valueVector.isInitialized) {
+            valueVector = initialValue.newInstance()
+            velocityVector = initialVelocity.newInstance()
+
+            times = FloatArray(timestamps.size) {
+                timestamps[it].toFloat() / SecondsToMillis
+            }
+
+            modes = IntArray(timestamps.size) {
+                val mode = (keyframes[timestamps[it]]?.arcMode ?: initialArcMode)
+                if (mode != ArcMode.Companion.ArcLinear) {
+                    requiresArcSpline = true
+                }
+
+                mode.value
+            }
+        }
+
+        if (!requiresArcSpline) {
+            return
+        }
+
+        // Initialize variables dependent on initial and/or target value
+        if (!::arcSpline.isInitialized ||
+            lastInitialValue != initialValue || lastTargetValue != targetValue
+        ) {
+            lastInitialValue = initialValue
+            lastTargetValue = targetValue
+
+            // Force to the next even dimension
+            val dimensionCount = initialValue.size % 2 + initialValue.size
+            posArray = FloatArray(dimensionCount)
+            slopeArray = FloatArray(dimensionCount)
+
+            // TODO(b/299477780): Re-use objects, after the first pass, only the initial and target
+            //  may change, and only if the keyframes does not overwrite it
+            val values = Array(timestamps.size) {
+                when (val timestamp = timestamps[it]) {
+                    // Start (zero) and end (durationMillis) may not have been declared in keyframes
+                    0 -> {
+                        if (!keyframes.contains(timestamp)) {
+                            FloatArray(dimensionCount, initialValue::get)
+                        } else {
+                            FloatArray(dimensionCount, keyframes[timestamp]!!.vectorValue::get)
+                        }
+                    }
+
+                    durationMillis -> {
+                        if (!keyframes.contains(timestamp)) {
+                            FloatArray(dimensionCount, targetValue::get)
+                        } else {
+                            FloatArray(dimensionCount, keyframes[timestamp]!!.vectorValue::get)
+                        }
+                    }
+
+                    // All other values are guaranteed to exist
+                    else -> FloatArray(dimensionCount, keyframes[timestamp]!!.vectorValue::get)
+                }
+            }
+            arcSpline = ArcSpline(
+                arcModes = modes,
+                timePoints = times,
+                y = values
+            )
+        }
+    }
+
+    /**
+     * @Throws IllegalStateException When the initial or final value to animate within a keyframe is
+     * missing.
+     */
     override fun getValueFromNanos(
         playTimeNanos: Long,
         initialValue: V,
@@ -240,49 +381,65 @@
     ): V {
         val playTimeMillis = playTimeNanos / MillisToNanos
         val clampedPlayTime = clampPlayTime(playTimeMillis).toInt()
+
         // If there is a key frame defined with the given time stamp, return that value
-        if (keyframes.containsKey(clampedPlayTime)) {
-            return keyframes.getValue(clampedPlayTime).first
+        if (keyframes.contains(clampedPlayTime)) {
+            return keyframes[clampedPlayTime]!!.vectorValue
         }
 
         if (clampedPlayTime >= durationMillis) {
             return targetValue
         } else if (clampedPlayTime <= 0) return initialValue
 
-        var startTime = 0
-        var startVal = initialValue
-        var endVal = targetValue
-        var endTime: Int = durationMillis
-        var easing: Easing = LinearEasing
-        for ((timestamp, value) in keyframes) {
-            if (clampedPlayTime > timestamp && timestamp >= startTime) {
-                startTime = timestamp
-                startVal = value.first
-                easing = value.second
-            } else if (clampedPlayTime < timestamp && timestamp <= endTime) {
-                endTime = timestamp
-                endVal = value.first
+        init(initialValue, targetValue, initialVelocity)
+
+        // ArcSpline is only initialized when necessary
+        if (::arcSpline.isInitialized) {
+            // ArcSpline requires eased play time in seconds
+            val easedTime = getEasedTime(clampedPlayTime)
+
+            arcSpline.getPos(
+                time = easedTime,
+                v = posArray
+            )
+            for (i in posArray.indices) {
+                valueVector[i] = posArray[i]
             }
+            return valueVector
         }
 
-        // Now interpolate
-        val fraction = easing.transform(
-            (clampedPlayTime - startTime) / (endTime - startTime).toFloat()
-        )
-        init(initialValue)
-        for (i in 0 until startVal.size) {
-            valueVector[i] = lerp(startVal[i], endVal[i], fraction)
+        // If ArcSpline is not required we do a simple linear interpolation
+        val index = findEntryForTimeMillis(clampedPlayTime)
+
+        // For the `lerp` method we need the eased time as a fraction
+        val easedTime = getEasedTimeFromIndex(index, clampedPlayTime, true)
+
+        val timestampStart = timestamps[index]
+        val startValue: V = if (keyframes.contains(timestampStart)) {
+            keyframes[timestampStart]!!.vectorValue
+        } else if (index == 0) {
+            // Use initial value if it wasn't overwritten by the user
+            initialValue
+        } else {
+            throw IllegalStateException("No value to animate from at $clampedPlayTime millis")
+        }
+
+        val timestampEnd = timestamps[index + 1]
+        val endValue = if (keyframes.contains(timestampEnd)) {
+            keyframes[timestampEnd]!!.vectorValue
+        } else if (index + 1 == timestamps.size - 1) {
+            // Use target value if it wasn't overwritten by the user
+            targetValue
+        } else {
+            throw IllegalStateException("No value to animate to at $clampedPlayTime millis")
+        }
+
+        for (i in 0 until valueVector.size) {
+            valueVector[i] = lerp(startValue[i], endValue[i], easedTime)
         }
         return valueVector
     }
 
-    private fun init(value: V) {
-        if (!::valueVector.isInitialized) {
-            valueVector = value.newInstance()
-            velocityVector = value.newInstance()
-        }
-    }
-
     override fun getVelocityFromNanos(
         playTimeNanos: Long,
         initialValue: V,
@@ -291,9 +448,26 @@
     ): V {
         val playTimeMillis = playTimeNanos / MillisToNanos
         val clampedPlayTime = clampPlayTime(playTimeMillis)
-        if (clampedPlayTime <= 0L) {
+        if (clampedPlayTime < 0L) {
             return initialVelocity
         }
+
+        init(initialValue, targetValue, initialVelocity)
+
+        // ArcSpline is only initialized when necessary
+        if (::arcSpline.isInitialized) {
+            val easedTime = getEasedTime(clampedPlayTime.toInt())
+            arcSpline.getSlope(
+                time = easedTime,
+                v = slopeArray
+            )
+            for (i in slopeArray.indices) {
+                velocityVector[i] = slopeArray[i]
+            }
+            return velocityVector
+        }
+
+        // Velocity calculation when ArcSpline is not used
         val startNum = getValueFromMillis(
             clampedPlayTime - 1,
             initialValue,
@@ -306,13 +480,113 @@
             targetValue,
             initialVelocity
         )
-
-        init(initialValue)
         for (i in 0 until startNum.size) {
             velocityVector[i] = (startNum[i] - endNum[i]) * 1000f
         }
         return velocityVector
     }
+
+    private fun getEasedTime(timeMillis: Int): Float {
+        // There's no promise on the nature of the given time, so we need to search for the correct
+        // time range at every call
+        val index = findEntryForTimeMillis(timeMillis)
+        return getEasedTimeFromIndex(index, timeMillis, false)
+    }
+
+    private fun getEasedTimeFromIndex(
+        index: Int,
+        timeMillis: Int,
+        asFraction: Boolean
+    ): Float {
+        if (index >= timestamps.lastIndex) {
+            // Return the same value. This may only happen at the end of the animation.
+            return timeMillis.toFloat() / SecondsToMillis
+        }
+        val timeMin = timestamps[index]
+        val timeMax = timestamps[index + 1]
+
+        if (timeMillis == timeMin) {
+            return timeMin.toFloat() / SecondsToMillis
+        }
+
+        val timeRange = timeMax - timeMin
+        val easing = keyframes[timeMin]?.easing ?: defaultEasing
+        val rawFraction = (timeMillis - timeMin).toFloat() / timeRange
+        val easedFraction = easing.transform(rawFraction)
+
+        if (asFraction) {
+            return easedFraction
+        }
+        return (timeRange * easedFraction + timeMin) / SecondsToMillis
+    }
+
+    /**
+     * Returns the entry index such that:
+     *
+     * [timeMillis] >= Entry(i).key && [timeMillis] < Entry(i+1).key
+     */
+    private fun findEntryForTimeMillis(timeMillis: Int): Int {
+        val index = timestamps.binarySearch(timeMillis)
+        return if (index < -1) -(index + 2) else index
+    }
+
+    @Suppress("unused")
+    private val ArcMode.value: Int
+        get() = when (this) {
+            ArcMode.Companion.ArcAbove -> ArcSpline.ArcAbove
+            ArcMode.Companion.ArcBelow -> ArcSpline.ArcBelow
+            ArcMode.Companion.ArcLinear -> ArcSpline.ArcStartLinear
+            else -> ArcSpline.ArcStartLinear // Unknown mode, fallback to linear
+        }
+}
+
+@OptIn(ExperimentalAnimationSpecApi::class)
+internal data class VectorizedKeyframeSpecElementInfo<V : AnimationVector>(
+    val vectorValue: V,
+    val easing: Easing,
+    val arcMode: ArcMode
+)
+
+/**
+ * Interpolation mode for Arc-based animation spec.
+ *
+ * @see ArcAbove
+ * @see ArcBelow
+ * @see ArcLinear
+ *
+ * @see ArcAnimationSpec
+ */
+@ExperimentalAnimationSpecApi
+sealed class ArcMode {
+    companion object {
+        /**
+         * Interpolates using a quarter of an Ellipse where the curve is "above" the center of the
+         * Ellipse.
+         */
+        @ExperimentalAnimationSpecApi
+        object ArcAbove : ArcMode()
+
+        /**
+         * Interpolates using a quarter of an Ellipse where the curve is "below" the center of the
+         * Ellipse.
+         */
+        @ExperimentalAnimationSpecApi
+        object ArcBelow : ArcMode()
+
+        /**
+         * An [ArcMode] that forces linear interpolation.
+         *
+         * You'll likely only use this mode within a keyframe.
+         */
+        @ExperimentalAnimationSpecApi
+        object ArcLinear : ArcMode()
+
+        /**
+         * Unused [ArcMode] to prevent exhaustive `when` usage.
+         */
+        @Suppress("unused")
+        private object UnexpectedArc : ArcMode()
+    }
 }
 
 /**
diff --git a/compose/animation/animation-graphics/build.gradle b/compose/animation/animation-graphics/build.gradle
index 7d96f6d..648dad7 100644
--- a/compose/animation/animation-graphics/build.gradle
+++ b/compose/animation/animation-graphics/build.gradle
@@ -52,7 +52,7 @@
         }
         androidMain.dependencies {
             api("androidx.annotation:annotation:1.1.0")
-            api(project(":annotation:annotation-experimental"))
+            api("androidx.annotation:annotation-experimental:1.4.0")
             implementation("androidx.core:core-ktx:1.5.0")
         }
 
diff --git a/compose/animation/animation/build.gradle b/compose/animation/animation/build.gradle
index 578889e..643b18e 100644
--- a/compose/animation/animation/build.gradle
+++ b/compose/animation/animation/build.gradle
@@ -67,7 +67,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api("androidx.annotation:annotation:1.1.0")
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
             }
         }
 
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
index c138d13..40b35d5 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
@@ -60,6 +60,7 @@
 import androidx.compose.animation.demos.statetransition.LoadingAnimationDemo
 import androidx.compose.animation.demos.statetransition.MultiDimensionalAnimationDemo
 import androidx.compose.animation.demos.statetransition.RepeatedRotationDemo
+import androidx.compose.animation.demos.suspendfun.ArcOffsetDemo
 import androidx.compose.animation.demos.suspendfun.InfiniteAnimationDemo
 import androidx.compose.animation.demos.suspendfun.OffsetKeyframeSplinePlaygroundDemo
 import androidx.compose.animation.demos.suspendfun.OffsetKeyframeWithSplineDemo
@@ -166,6 +167,7 @@
                 ComposableDemo("Spline Keyframes Playground") {
                     OffsetKeyframeSplinePlaygroundDemo()
                 },
+                ComposableDemo("Arc Offset Demo") { ArcOffsetDemo() },
             )
         ),
         DemoCategory(
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/ArcOffsetDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/ArcOffsetDemo.kt
new file mode 100644
index 0000000..ea765b4
--- /dev/null
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/ArcOffsetDemo.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.demos.suspendfun
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.ArcMode.Companion.ArcAbove
+import androidx.compose.animation.core.ArcMode.Companion.ArcBelow
+import androidx.compose.animation.core.ArcMode.Companion.ArcLinear
+import androidx.compose.animation.core.ExperimentalAnimationSpecApi
+import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.animation.core.keyframes
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathEffect
+import androidx.compose.ui.graphics.PointMode
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.drawscope.translate
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.round
+import androidx.compose.ui.unit.toSize
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.collectLatest
+
+@SuppressWarnings("PrimitiveInCollection")
+@OptIn(ExperimentalAnimationSpecApi::class)
+@Preview
+@Composable
+fun ArcOffsetDemo() {
+    val animOffset = remember { Animatable(Offset.Zero, Offset.VectorConverter) }
+    val points = remember { mutableStateListOf<Offset>() }
+    val target = remember { MutableStateFlow(Offset.Unspecified) }
+    Box(
+        Modifier
+            .fillMaxSize()
+            .pointerInput(Unit) {
+                val halfSize = size.toSize() * 0.5f
+                detectTapGestures {
+                    target.value = Offset(
+                        x = it.x - halfSize.width,
+                        y = it.y - halfSize.height
+                    )
+                }
+            }
+            .drawBehind {
+                val halfSize = size * 0.5f
+                translate(halfSize.width, halfSize.height) {
+                    drawPoints(
+                        points = points,
+                        pointMode = PointMode.Lines,
+                        color = Color(0xFFFFC107),
+                        strokeWidth = 4f,
+                        pathEffect = PathEffect.dashPathEffect(floatArrayOf(4f, 2f)),
+                        cap = StrokeCap.Round
+                    )
+                }
+            }
+    ) {
+        Text("Tap anywhere to animate")
+        Box(
+            Modifier
+                .size(50.dp)
+                .align(Alignment.Center)
+                .offset {
+                    animOffset.value.round()
+                }
+                .background(Color.Red, RoundedCornerShape(50))
+        )
+    }
+
+    LaunchedEffect(Unit) {
+        target.collectLatest { target ->
+            if (target != Offset.Unspecified) {
+                points.clear()
+                val current = animOffset.value
+                val diffOff = target - current
+                val halfDiff = diffOff * 0.5f
+                val midOffset = current + halfDiff
+                val mode = if (diffOff.y > 0f) ArcBelow else ArcAbove
+                animOffset.animateTo(
+                    targetValue = target,
+                    animationSpec = keyframes {
+                        durationMillis = 1400
+
+                        current atFraction 0f using LinearEasing using ArcLinear
+                        midOffset atFraction 0.5f using FastOutSlowInEasing using mode
+                    }
+                ) {
+                    points.add(value)
+                }
+            }
+        }
+    }
+}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
index 55b2040..7e19a0f 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
@@ -31,6 +31,7 @@
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, true)
         put(ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY, true)
+        put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
     }
 
     @JvmField
@@ -180,6 +181,11 @@
             ) {
                 "${it.groupValues[1]}\"${generateSourceInfo(it.groupValues[2], source)}\")"
             }
+            .replace(
+                Regex("(rememberComposableLambda[N]?)\\((-?\\d+)")
+            ) {
+                "${it.groupValues[1]}(<>"
+            }
             // replace source keys for joinKey calls
             .replace(
                 Regex(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
index 479d52f..01f91c7 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
@@ -1641,7 +1641,7 @@
             expectedTransformed = """
                 @StabilityInferred(parameters = 1)
                 class CombinedUnstable<T> (val first: T, val second: ParametrizedFoo<SomeFoo>) {
-                  static val %stable: Int = ParametrizedFoo.%stable or 0
+                  static val %stable: Int = ParametrizedFoo.%stable
                 }
             """
         )
@@ -1660,7 +1660,7 @@
             expectedTransformed = """
             @StabilityInferred(parameters = 1)
             class CombinedStable<T> (val first: T, val second: ParametrizedFoo<SomeFoo>) {
-              static val %stable: Int = SomeFoo.%stable or ParametrizedFoo.%stable or 0
+              static val %stable: Int = SomeFoo.%stable or ParametrizedFoo.%stable
             }
         """
         )
@@ -1679,7 +1679,7 @@
             expectedTransformed = """
             @StabilityInferred(parameters = 3)
             class CombinedStable<T, K> (val first: T, val second: ParametrizedFoo<K>) {
-              static val %stable: Int = 0 or ParametrizedFoo.%stable or 0
+              static val %stable: Int = ParametrizedFoo.%stable
             }
         """
         )
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
index f5a7fe5..f22ad2e 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
@@ -450,7 +450,7 @@
                     }
                 }
                 Text("Some more text")
-            } 
+            }
         """
     )
 
@@ -2437,4 +2437,44 @@
             }
         """
     )
+
+    @Test
+    fun testLambdaWithNonUnitResult() = verifyGoldenComposeIrTransform(
+        """
+            import androidx.compose.runtime.*
+
+            @Composable
+            fun Test() {
+                val factory = createFactory {
+                    10
+                }
+                factory()
+            }
+        """,
+        extra = """
+            import androidx.compose.runtime.*
+
+            fun createFactory(factory: @Composable () -> Int) = factory
+        """
+    )
+
+    @Test
+    fun testOverrideWithNonUnitResult() = verifyGoldenComposeIrTransform(
+        """
+            import androidx.compose.runtime.*
+
+            class SomeClassImpl: SomeClass() {
+                @Composable
+                override fun SomeFunction(): Int = 10
+            }
+        """,
+        """
+            import androidx.compose.runtime.*
+
+            abstract class SomeClass {
+                @Composable
+                abstract fun SomeFunction(): Int
+            }
+        """
+    )
 }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
index 252e8565..052076b 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
@@ -24,6 +24,7 @@
 ) : AbstractControlFlowTransformTests(useFir) {
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, false)
+        put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
         put(ComposeConfiguration.TRACE_MARKERS_ENABLED_KEY, false)
     }
 
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/GoldenTransformRule.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/GoldenTransformRule.kt
index 8ef8565..1263728 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/GoldenTransformRule.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/GoldenTransformRule.kt
@@ -28,6 +28,7 @@
 private const val ENV_GENERATE_GOLDEN = "GENERATE_GOLDEN"
 private const val GOLDEN_FILE_TYPE = "txt"
 private fun env(name: String): Boolean = (System.getenv(name) ?: "false").toBoolean()
+private fun envList(name: String): List<String> = (System.getenv(name) ?: "").quotedSplit()
 
 /**
  * GoldenTransformRule
@@ -38,11 +39,14 @@
  *
  * @param pathToGoldens: Path to golden files
  * @param generateGoldens: When true, will generate the golden test file and replace any existing
+ * @param generateGoldenFiles: Generate the golden file if the name (without extension, is in the
+ *          list.
  * @param generateMissingGoldens: When true, will generate a golden file for any that are not found.
  **/
 class GoldenTransformRule(
     private val pathToGoldens: String,
     private val generateGoldens: Boolean = env(ENV_GENERATE_GOLDEN),
+    private val generateGoldenFiles: Set<String> = envList(ENV_GENERATE_GOLDEN).toSet(),
     private val generateMissingGoldens: Boolean = true
 ) : TestRule {
     private lateinit var goldenFile: File
@@ -72,7 +76,10 @@
      * If generateGoldens is true, the golden file will first be generated.
      */
     fun verifyGolden(testInfo: GoldenTransformTestInfo) {
-        if (generateGoldens || (!goldenFile.exists() && generateMissingGoldens)) {
+        if (
+            generateGoldens || (!goldenFile.exists() && generateMissingGoldens) ||
+            goldenFile.nameWithoutExtension in generateGoldenFiles
+        ) {
             saveGolden(testInfo)
         }
 
@@ -90,7 +97,11 @@
         Assert.assertEquals(
             "Transformed source does not match golden file:" +
                 "\n${goldenFile.absolutePath}\n" +
-                "To regenerate golden files, pass GENERATE_GOLDEN=true as an env variable.",
+                "To regenerate golden files, set GENERATE_GOLDEN=\"${
+                    goldenFile.nameWithoutExtension}\" as an env variable (or set it to 'true' " +
+                "to generate all the files).\n" +
+                "The environment variable can be a comma delimited list of names (the quotes are " +
+                "optional)",
             loadedTestInfo.transformed,
             testInfo.transformed
         )
@@ -151,3 +162,34 @@
         }
     }
 }
+
+private fun String.quotedSplit(): List<String> {
+    val result = mutableListOf<String>()
+    var current = 0
+
+    while (current < length) {
+        var start = current
+        var end: Int
+        when (get(current)) {
+            ' ', '\n', '\r', ',' -> {
+                current++
+                continue
+            }
+            '"' -> {
+                start = ++current
+                while (current < length && get(current) != '"') {
+                    current++
+                }
+                end = current++
+            }
+            else -> {
+                while (current < length && get(current) != ',') {
+                    current++
+                }
+                end = current++
+            }
+        }
+        result.add(substring(start, end))
+    }
+    return result
+}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
index 4164c6d..54c4b6c 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
@@ -25,6 +25,7 @@
 class LambdaMemoizationTransformTests(useFir: Boolean) : AbstractIrTransformTest(useFir) {
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, true)
+        put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
         languageVersionSettings = LanguageVersionSettingsImpl(
             languageVersion = languageVersionSettings.languageVersion,
             apiVersion = languageVersionSettings.apiVersion,
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
index daca294..105e2eb 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
@@ -24,6 +24,7 @@
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, true)
         put(ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY, true)
+        put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
     }
 
     private fun comparisonPropagation(
@@ -793,6 +794,7 @@
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, true)
         put(ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY, true)
+        put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
         put(ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY, true)
     }
 
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
index d66d65b5..760ded6 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
@@ -47,6 +47,7 @@
             ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY,
             intrinsicRememberEnabled
         )
+        put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
     }
 
     @Test
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testLocalParameterBasedTypeParameterSubstitution\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testLocalParameterBasedTypeParameterSubstitution\133useFir = false\135.txt"
index c41e4eb..3a7e657 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testLocalParameterBasedTypeParameterSubstitution\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testLocalParameterBasedTypeParameterSubstitution\133useFir = false\135.txt"
@@ -116,7 +116,7 @@
         traceEventStart(<>, %dirty, -1, <>)
       }
       A(item, %composer, 0b1110 and %dirty)
-      A(Wrapper(item), %composer, Wrapper.%stable or 0)
+      A(Wrapper(item), %composer, Wrapper.%stable)
       if (isTraceInProgress()) {
         traceEventEnd()
       }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testLocalParameterBasedTypeParameterSubstitution\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testLocalParameterBasedTypeParameterSubstitution\133useFir = true\135.txt"
index c41e4eb..3a7e657 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testLocalParameterBasedTypeParameterSubstitution\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testLocalParameterBasedTypeParameterSubstitution\133useFir = true\135.txt"
@@ -116,7 +116,7 @@
         traceEventStart(<>, %dirty, -1, <>)
       }
       A(item, %composer, 0b1110 and %dirty)
-      A(Wrapper(item), %composer, Wrapper.%stable or 0)
+      A(Wrapper(item), %composer, Wrapper.%stable)
       if (isTraceInProgress()) {
         traceEventEnd()
       }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationOfVariousTypesInSameModule\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationOfVariousTypesInSameModule\133useFir = false\135.txt"
index aaf1993..a3138d7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationOfVariousTypesInSameModule\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationOfVariousTypesInSameModule\133useFir = false\135.txt"
@@ -68,7 +68,7 @@
     A(X(listOf(StableClass())), %composer, 0)
     A(StableDelegateProp(), %composer, 0)
     A(UnstableDelegateProp(), %composer, UnstableDelegate.%stable)
-    A(SingleParamProp(0), %composer, SingleParamProp.%stable or 0)
+    A(SingleParamProp(0), %composer, SingleParamProp.%stable)
     A(SingleParamNonProp(0), %composer, SingleParamNonProp.%stable)
     A(SingleParamProp(Any()), %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationOfVariousTypesInSameModule\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationOfVariousTypesInSameModule\133useFir = true\135.txt"
index aaf1993..a3138d7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationOfVariousTypesInSameModule\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationOfVariousTypesInSameModule\133useFir = true\135.txt"
@@ -68,7 +68,7 @@
     A(X(listOf(StableClass())), %composer, 0)
     A(StableDelegateProp(), %composer, 0)
     A(UnstableDelegateProp(), %composer, UnstableDelegate.%stable)
-    A(SingleParamProp(0), %composer, SingleParamProp.%stable or 0)
+    A(SingleParamProp(0), %composer, SingleParamProp.%stable)
     A(SingleParamNonProp(0), %composer, SingleParamNonProp.%stable)
     A(SingleParamProp(Any()), %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParamsSameModule\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParamsSameModule\133useFir = false\135.txt"
index 1a3e797..bb6bc32c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParamsSameModule\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParamsSameModule\133useFir = false\135.txt"
@@ -91,7 +91,7 @@
 
 @StabilityInferred(parameters = -1)
 class Foo<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31, T32, T33> (val t1: T1, val t2: T2, val t3: T3, val t4: T4, val t5: T5, val t6: T6, val t7: T7, val t8: T8, val t9: T9, val t10: T10, val t11: T11, val t12: T12, val t13: T13, val t14: T14, val t15: T15, val t16: T16, val t17: T17, val t18: T18, val t19: T19, val t20: T20, val t21: T21, val t22: T22, val t23: T23, val t24: T24, val t25: T25, val t26: T26, val t27: T27, val t28: T28, val t29: T29, val t30: T30, val t31: T31, val t32: T32, val t33: T33) {
-  static val %stable: Int = 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0
+  static val %stable: Int = 0
 }
 fun used(any: Any? = null) { }
 @Composable
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParamsSameModule\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParamsSameModule\133useFir = true\135.txt"
index 1a3e797..bb6bc32c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParamsSameModule\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParamsSameModule\133useFir = true\135.txt"
@@ -91,7 +91,7 @@
 
 @StabilityInferred(parameters = -1)
 class Foo<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31, T32, T33> (val t1: T1, val t2: T2, val t3: T3, val t4: T4, val t5: T5, val t6: T6, val t7: T7, val t8: T8, val t9: T9, val t10: T10, val t11: T11, val t12: T12, val t13: T13, val t14: T14, val t15: T15, val t16: T16, val t17: T17, val t18: T18, val t19: T19, val t20: T20, val t21: T21, val t22: T22, val t23: T23, val t24: T24, val t25: T25, val t26: T26, val t27: T27, val t28: T28, val t29: T29, val t30: T30, val t31: T31, val t32: T32, val t33: T33) {
-  static val %stable: Int = 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0
+  static val %stable: Int = 0
 }
 fun used(any: Any? = null) { }
 @Composable
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParams\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParams\133useFir = false\135.txt"
index 1e357c1..7c6f1e5 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParams\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParams\133useFir = false\135.txt"
@@ -69,7 +69,7 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(y)
-    A(Foo(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true), %composer, Foo.%stable or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0, 0)
+    A(Foo(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true), %composer, Foo.%stable, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParams\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParams\133useFir = true\135.txt"
index 1e357c1..7c6f1e5 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParams\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testStabilityPropagationTooManyTypeParams\133useFir = true\135.txt"
@@ -69,7 +69,7 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(y)
-    A(Foo(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true), %composer, Foo.%stable or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0 or 0, 0)
+    A(Foo(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true), %composer, Foo.%stable, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testTransformInternalClasses\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testTransformInternalClasses\133useFir = false\135.txt"
index 45b77c2..39173c9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testTransformInternalClasses\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testTransformInternalClasses\133useFir = false\135.txt"
@@ -20,5 +20,5 @@
 }
 @StabilityInferred(parameters = 3)
 internal class MultipleFoo<K, T> (val value: K, val param: T) {
-  static val %stable: Int = 0 or 0
+  static val %stable: Int = 0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testTransformInternalClasses\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testTransformInternalClasses\133useFir = true\135.txt"
index 45b77c2..39173c9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testTransformInternalClasses\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ClassStabilityTransformTests/testTransformInternalClasses\133useFir = true\135.txt"
@@ -20,5 +20,5 @@
 }
 @StabilityInferred(parameters = 3)
 internal class MultipleFoo<K, T> (val value: K, val param: T) {
-  static val %stable: Int = 0 or 0
+  static val %stable: Int = 0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCallingProperties\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCallingProperties\133useFir = false\135.txt"
index 28702a3..dc0b8cf 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCallingProperties\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCallingProperties\133useFir = false\135.txt"
@@ -26,8 +26,7 @@
 val bar: Int
   @Composable @JvmName(name = "getBar")
   get() {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C:Test.kt#2487m")
+    sourceInformationMarkerStart(%composer, <>, "C:Test.kt#2487m")
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
@@ -35,14 +34,13 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     return tmp0
   }
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<bar>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<bar>:Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -50,5 +48,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCallingProperties\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCallingProperties\133useFir = true\135.txt"
index 28702a3..dc0b8cf 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCallingProperties\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCallingProperties\133useFir = true\135.txt"
@@ -26,8 +26,7 @@
 val bar: Int
   @Composable @JvmName(name = "getBar")
   get() {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C:Test.kt#2487m")
+    sourceInformationMarkerStart(%composer, <>, "C:Test.kt#2487m")
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
@@ -35,14 +34,13 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     return tmp0
   }
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<bar>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<bar>:Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -50,5 +48,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCircularCall\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCircularCall\133useFir = false\135.txt"
index d352454..0e43970 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCircularCall\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCircularCall\133useFir = false\135.txt"
@@ -25,8 +25,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<Exampl...>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<Exampl...>:Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -34,5 +33,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCircularCall\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCircularCall\133useFir = true\135.txt"
index d352454..0e43970 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCircularCall\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testCircularCall\133useFir = true\135.txt"
@@ -25,8 +25,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<Exampl...>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<Exampl...>:Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -34,5 +33,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testComposableNestedCall\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testComposableNestedCall\133useFir = false\135.txt"
index 9605088..5410db5 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testComposableNestedCall\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testComposableNestedCall\133useFir = false\135.txt"
@@ -67,8 +67,7 @@
 @Composable
 @ComposableInferredTarget(scheme = "[0[0]]")
 fun emit(composable: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(emit)<compos...>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "CC(emit)<compos...>:Test.kt#2487m")
   composable(%composer, 0b1110 and %changed)
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testComposableNestedCall\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testComposableNestedCall\133useFir = true\135.txt"
index 9605088..5410db5 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testComposableNestedCall\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testComposableNestedCall\133useFir = true\135.txt"
@@ -67,8 +67,7 @@
 @Composable
 @ComposableInferredTarget(scheme = "[0[0]]")
 fun emit(composable: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(emit)<compos...>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "CC(emit)<compos...>:Test.kt#2487m")
   composable(%composer, 0b1110 and %changed)
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDelegateCall\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDelegateCall\133useFir = false\135.txt"
index 346633b..9e6e09c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDelegateCall\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDelegateCall\133useFir = false\135.txt"
@@ -49,8 +49,7 @@
 }
 @Composable
 fun Foo.getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): Foo {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(getValue)P(1):Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(getValue)P(1):Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -58,15 +57,14 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @StabilityInferred(parameters = 1)
 class FooDelegate {
   @Composable
   fun getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): FooDelegate {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C(getValue)P(1):Test.kt#2487m")
+    sourceInformationMarkerStart(%composer, <>, "C(getValue)P(1):Test.kt#2487m")
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
@@ -74,7 +72,7 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     return tmp0
   }
   static val %stable: Int = 0
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDelegateCall\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDelegateCall\133useFir = true\135.txt"
index 346633b..9e6e09c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDelegateCall\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDelegateCall\133useFir = true\135.txt"
@@ -49,8 +49,7 @@
 }
 @Composable
 fun Foo.getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): Foo {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(getValue)P(1):Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(getValue)P(1):Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -58,15 +57,14 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @StabilityInferred(parameters = 1)
 class FooDelegate {
   @Composable
   fun getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): FooDelegate {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C(getValue)P(1):Test.kt#2487m")
+    sourceInformationMarkerStart(%composer, <>, "C(getValue)P(1):Test.kt#2487m")
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
@@ -74,7 +72,7 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     return tmp0
   }
   static val %stable: Int = 0
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDexNaming\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDexNaming\133useFir = false\135.txt"
index 8d77b7a..22f4969 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDexNaming\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDexNaming\133useFir = false\135.txt"
@@ -24,8 +24,7 @@
 val myProperty: Function0<Unit>
   @Composable @JvmName(name = "getMyProperty")
   get() {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C:Test.kt#2487m")
+    sourceInformationMarkerStart(%composer, <>, "C:Test.kt#2487m")
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
@@ -34,6 +33,6 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     return tmp0
   }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDexNaming\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDexNaming\133useFir = true\135.txt"
index 8d77b7a..22f4969 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDexNaming\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testDexNaming\133useFir = true\135.txt"
@@ -24,8 +24,7 @@
 val myProperty: Function0<Unit>
   @Composable @JvmName(name = "getMyProperty")
   get() {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C:Test.kt#2487m")
+    sourceInformationMarkerStart(%composer, <>, "C:Test.kt#2487m")
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
@@ -34,6 +33,6 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     return tmp0
   }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testInlineCall\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testInlineCall\133useFir = false\135.txt"
index 32e6ba3..501008c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testInlineCall\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testInlineCall\133useFir = false\135.txt"
@@ -29,16 +29,14 @@
 @Composable
 @ComposableInferredTarget(scheme = "[0[0]]")
 fun Example(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(Example)<conten...>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "CC(Example)<conten...>:Test.kt#2487m")
   content(%composer, 0b1110 and %changed)
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @NonRestartableComposable
 @Composable
 fun Test(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<Exampl...>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<Exampl...>:Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -50,5 +48,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testInlineCall\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testInlineCall\133useFir = true\135.txt"
index 32e6ba3..501008c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testInlineCall\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testInlineCall\133useFir = true\135.txt"
@@ -29,16 +29,14 @@
 @Composable
 @ComposableInferredTarget(scheme = "[0[0]]")
 fun Example(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(Example)<conten...>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "CC(Example)<conten...>:Test.kt#2487m")
   content(%composer, 0b1110 and %changed)
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @NonRestartableComposable
 @Composable
 fun Test(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<Exampl...>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<Exampl...>:Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -50,5 +48,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testKeyCall\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testKeyCall\133useFir = false\135.txt"
index 22fcfc7..d71903b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testKeyCall\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testKeyCall\133useFir = false\135.txt"
@@ -98,8 +98,8 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     %composer.startMovableGroup(<>, value)
-    sourceInformation(%composer, "<Wrappe...>")
-    Wrapper(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    sourceInformation(%composer, "<{>,<Wrappe...>")
+    Wrapper(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<Leaf("...>:Test.kt#2487m")
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -112,7 +112,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b0110)
+    }, %composer, 0b00110110), %composer, 0b0110)
     %composer.endMovableGroup()
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testKeyCall\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testKeyCall\133useFir = true\135.txt"
index 22fcfc7..d71903b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testKeyCall\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testKeyCall\133useFir = true\135.txt"
@@ -98,8 +98,8 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     %composer.startMovableGroup(<>, value)
-    sourceInformation(%composer, "<Wrappe...>")
-    Wrapper(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    sourceInformation(%composer, "<{>,<Wrappe...>")
+    Wrapper(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<Leaf("...>:Test.kt#2487m")
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -112,7 +112,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b0110)
+    }, %composer, 0b00110110), %composer, 0b0110)
     %composer.endMovableGroup()
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testLocalClassAndObjectLiterals\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testLocalClassAndObjectLiterals\133useFir = false\135.txt"
index 1dabca6..f528f4e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testLocalClassAndObjectLiterals\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testLocalClassAndObjectLiterals\133useFir = false\135.txt"
@@ -38,21 +38,19 @@
 @NonRestartableComposable
 @Composable
 fun Wat(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Wat):Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Wat):Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @NonRestartableComposable
 @Composable
 fun Foo(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Foo)<Wat()>,<goo()>,<baz()>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Foo)<Wat()>,<goo()>,<baz()>:Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -60,8 +58,7 @@
   @NonRestartableComposable
   @Composable
   fun goo(%composer: Composer?, %changed: Int) {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C(goo)<Wat()>:Test.kt#2487m")
+    sourceInformationMarkerStart(%composer, <>, "C(goo)<Wat()>:Test.kt#2487m")
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
@@ -69,14 +66,13 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
   }
   class Bar {
     @NonRestartableComposable
     @Composable
     fun baz(%composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(baz)<Wat()>:Test.kt#2487m")
+      sourceInformationMarkerStart(%composer, <>, "C(baz)<Wat()>:Test.kt#2487m")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
@@ -84,7 +80,7 @@
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
   }
   goo(%composer, 0)
@@ -92,5 +88,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testLocalClassAndObjectLiterals\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testLocalClassAndObjectLiterals\133useFir = true\135.txt"
index 1dabca6..f528f4e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testLocalClassAndObjectLiterals\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testLocalClassAndObjectLiterals\133useFir = true\135.txt"
@@ -38,21 +38,19 @@
 @NonRestartableComposable
 @Composable
 fun Wat(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Wat):Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Wat):Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @NonRestartableComposable
 @Composable
 fun Foo(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Foo)<Wat()>,<goo()>,<baz()>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Foo)<Wat()>,<goo()>,<baz()>:Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -60,8 +58,7 @@
   @NonRestartableComposable
   @Composable
   fun goo(%composer: Composer?, %changed: Int) {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C(goo)<Wat()>:Test.kt#2487m")
+    sourceInformationMarkerStart(%composer, <>, "C(goo)<Wat()>:Test.kt#2487m")
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
@@ -69,14 +66,13 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
   }
   class Bar {
     @NonRestartableComposable
     @Composable
     fun baz(%composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(baz)<Wat()>:Test.kt#2487m")
+      sourceInformationMarkerStart(%composer, <>, "C(baz)<Wat()>:Test.kt#2487m")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
@@ -84,7 +80,7 @@
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
   }
   goo(%composer, 0)
@@ -92,5 +88,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableDelegateCall\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableDelegateCall\133useFir = false\135.txt"
index aef3515..5ed7bbf 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableDelegateCall\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableDelegateCall\133useFir = false\135.txt"
@@ -36,10 +36,9 @@
 }
 @Composable
 fun Foo.getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): Foo {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(getValue)P(1):Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "CC(getValue)P(1):Test.kt#2487m")
   val tmp0 = <this>
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableDelegateCall\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableDelegateCall\133useFir = true\135.txt"
index aef3515..5ed7bbf 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableDelegateCall\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableDelegateCall\133useFir = true\135.txt"
@@ -36,10 +36,9 @@
 }
 @Composable
 fun Foo.getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): Foo {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(getValue)P(1):Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "CC(getValue)P(1):Test.kt#2487m")
   val tmp0 = <this>
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableVarargParams\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableVarargParams\133useFir = false\135.txt"
index 104b4c0..6df5018 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableVarargParams\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableVarargParams\133useFir = false\135.txt"
@@ -61,8 +61,7 @@
 @NonRestartableComposable
 @Composable
 fun Test(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<B(0,>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<B(0,>:Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -70,5 +69,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableVarargParams\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableVarargParams\133useFir = true\135.txt"
index 104b4c0..6df5018 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableVarargParams\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testStableVarargParams\133useFir = true\135.txt"
@@ -61,8 +61,7 @@
 @NonRestartableComposable
 @Composable
 fun Test(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<B(0,>:Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<B(0,>:Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -70,5 +69,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testUnstableDelegateCall\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testUnstableDelegateCall\133useFir = false\135.txt"
index 8bc3952..7d4053a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testUnstableDelegateCall\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testUnstableDelegateCall\133useFir = false\135.txt"
@@ -39,10 +39,9 @@
 }
 @Composable
 fun Foo.getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): Foo {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(getValue)P(1):Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "CC(getValue)P(1):Test.kt#2487m")
   val tmp0 = <this>
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testUnstableDelegateCall\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testUnstableDelegateCall\133useFir = true\135.txt"
index 8bc3952..7d4053a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testUnstableDelegateCall\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/testUnstableDelegateCall\133useFir = true\135.txt"
@@ -39,10 +39,9 @@
 }
 @Composable
 fun Foo.getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): Foo {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(getValue)P(1):Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "CC(getValue)P(1):Test.kt#2487m")
   val tmp0 = <this>
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/validateNoComposableFunctionReferencesInCalleeOverriddenSymbols\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/validateNoComposableFunctionReferencesInCalleeOverriddenSymbols\133useFir = false\135.txt"
index b1c2fbc..bf83025 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/validateNoComposableFunctionReferencesInCalleeOverriddenSymbols\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/validateNoComposableFunctionReferencesInCalleeOverriddenSymbols\133useFir = false\135.txt"
@@ -21,8 +21,7 @@
 @Composable
 @NonRestartableComposable
 fun Foo(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Foo):Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Foo):Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -30,5 +29,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/validateNoComposableFunctionReferencesInCalleeOverriddenSymbols\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/validateNoComposableFunctionReferencesInCalleeOverriddenSymbols\133useFir = true\135.txt"
index b1c2fbc..bf83025 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/validateNoComposableFunctionReferencesInCalleeOverriddenSymbols\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/validateNoComposableFunctionReferencesInCalleeOverriddenSymbols\133useFir = true\135.txt"
@@ -21,8 +21,7 @@
 @Composable
 @NonRestartableComposable
 fun Foo(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Foo):Test.kt#2487m")
+  sourceInformationMarkerStart(%composer, <>, "C(Foo):Test.kt#2487m")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -30,5 +29,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testAND\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testAND\133useFir = false\135.txt"
index b2ca142..f051992 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testAND\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testAND\133useFir = false\135.txt"
@@ -21,15 +21,17 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<B()>,<B()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<B()>,<B()>")
   val tmp0_group = B(%composer, 0) && B(%composer, 0)
+  %composer.endReplaceableGroup()
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testAND\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testAND\133useFir = true\135.txt"
index b2ca142..f051992 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testAND\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testAND\133useFir = true\135.txt"
@@ -21,15 +21,17 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<B()>,<B()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<B()>,<B()>")
   val tmp0_group = B(%composer, 0) && B(%composer, 0)
+  %composer.endReplaceableGroup()
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = false\135.txt"
index ee7eaac..a35941e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = false\135.txt"
@@ -23,25 +23,23 @@
 
 @Composable
 fun <T> provided(value: T, %composer: Composer?, %changed: Int): State<T> {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(provided)*<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(provided)*<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  val tmp0 = %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  val tmp0 = sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp1_group = %composer.cache(false) {
     mutableStateOf(
       value = value
     )
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp1_group.apply {
     %this%apply.value = value
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = true\135.txt"
index ee7eaac..a35941e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = true\135.txt"
@@ -23,25 +23,23 @@
 
 @Composable
 fun <T> provided(value: T, %composer: Composer?, %changed: Int): State<T> {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(provided)*<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(provided)*<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  val tmp0 = %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  val tmp0 = sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp1_group = %composer.cache(false) {
     mutableStateOf(
       value = value
     )
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp1_group.apply {
     %this%apply.value = value
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsAfter\133useFir = false\135.txt"
index c9497b4..d400019 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsAfter\133useFir = false\135.txt"
@@ -26,11 +26,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<P(i)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<P(i)>")
   while (items.hasNext()) {
     val i = items.next()
     if (i == 0) {
@@ -38,8 +39,9 @@
     }
     P(i, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsAfter\133useFir = true\135.txt"
index c9497b4..d400019 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsAfter\133useFir = true\135.txt"
@@ -26,11 +26,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<P(i)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<P(i)>")
   while (items.hasNext()) {
     val i = items.next()
     if (i == 0) {
@@ -38,8 +39,9 @@
     }
     P(i, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfterAndCallAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfterAndCallAfter\133useFir = false\135.txt"
index 1df8e47..0945fc9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfterAndCallAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfterAndCallAfter\133useFir = false\135.txt"
@@ -29,8 +29,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -49,5 +48,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfterAndCallAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfterAndCallAfter\133useFir = true\135.txt"
index 1df8e47..0945fc9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfterAndCallAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfterAndCallAfter\133useFir = true\135.txt"
@@ -29,8 +29,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -49,5 +48,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfter\133useFir = false\135.txt"
index c548530..bc9a8a3 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfter\133useFir = false\135.txt"
@@ -29,11 +29,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<P(i)>,<P(j)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<P(i)>,<P(j)>")
   while (items.hasNext()) {
     val i = items.next()
     val j = i
@@ -43,8 +44,9 @@
     }
     P(j, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfter\133useFir = true\135.txt"
index c548530..bc9a8a3 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBeforeAndAfter\133useFir = true\135.txt"
@@ -29,11 +29,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<P(i)>,<P(j)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<P(i)>,<P(j)>")
   while (items.hasNext()) {
     val i = items.next()
     val j = i
@@ -43,8 +44,9 @@
     }
     P(j, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBefore\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBefore\133useFir = false\135.txt"
index 8044240..3efd13f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBefore\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBefore\133useFir = false\135.txt"
@@ -26,11 +26,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<P(i)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<P(i)>")
   while (items.hasNext()) {
     val i = items.next()
     P(i, %composer, 0)
@@ -38,8 +39,9 @@
       break
     }
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBefore\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBefore\133useFir = true\135.txt"
index 8044240..3efd13f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBefore\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testBreakWithCallsBefore\133useFir = true\135.txt"
@@ -26,11 +26,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<P(i)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<P(i)>")
   while (items.hasNext()) {
     val i = items.next()
     P(i, %composer, 0)
@@ -38,8 +39,9 @@
       break
     }
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsAfter\133useFir = false\135.txt"
index 260958f..aca099e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsAfter\133useFir = false\135.txt"
@@ -26,11 +26,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   while (items.hasNext()) {
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "<P(i)>")
@@ -42,8 +43,9 @@
     P(i, %composer, 0)
     %composer.endReplaceableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsAfter\133useFir = true\135.txt"
index 260958f..aca099e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsAfter\133useFir = true\135.txt"
@@ -26,11 +26,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   while (items.hasNext()) {
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "<P(i)>")
@@ -42,8 +43,9 @@
     P(i, %composer, 0)
     %composer.endReplaceableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBeforeAndAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBeforeAndAfter\133useFir = false\135.txt"
index 5cd2f4c..ee4b2ed 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBeforeAndAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBeforeAndAfter\133useFir = false\135.txt"
@@ -27,11 +27,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   while (items.hasNext()) {
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "<P(i)>,<P(i)>")
@@ -44,8 +45,9 @@
     P(i, %composer, 0)
     %composer.endReplaceableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBeforeAndAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBeforeAndAfter\133useFir = true\135.txt"
index 5cd2f4c..ee4b2ed 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBeforeAndAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBeforeAndAfter\133useFir = true\135.txt"
@@ -27,11 +27,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   while (items.hasNext()) {
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "<P(i)>,<P(i)>")
@@ -44,8 +45,9 @@
     P(i, %composer, 0)
     %composer.endReplaceableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBefore\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBefore\133useFir = false\135.txt"
index 0942632..867c78d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBefore\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBefore\133useFir = false\135.txt"
@@ -27,11 +27,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   while (items.hasNext()) {
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "<P(i)>")
@@ -44,8 +45,9 @@
     print(i)
     %composer.endReplaceableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBefore\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBefore\133useFir = true\135.txt"
index 0942632..867c78d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBefore\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testContinueWithCallsBefore\133useFir = true\135.txt"
@@ -27,11 +27,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   while (items.hasNext()) {
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "<P(i)>")
@@ -44,8 +45,9 @@
     print(i)
     %composer.endReplaceableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testDynamicWrappingGroupWithReturnValue\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testDynamicWrappingGroupWithReturnValue\133useFir = false\135.txt"
index dc4f462..335a543 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testDynamicWrappingGroupWithReturnValue\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testDynamicWrappingGroupWithReturnValue\133useFir = false\135.txt"
@@ -24,12 +24,13 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = <block>{
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "")
     val tmp4_group = if (x > 0) {
       val tmp3_group = if (%composer.startReplaceableGroup(<>)
       sourceInformation(%composer, "<B()>")
@@ -44,11 +45,12 @@
     } else {
       4
     }
+    %composer.endReplaceableGroup()
     tmp4_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testDynamicWrappingGroupWithReturnValue\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testDynamicWrappingGroupWithReturnValue\133useFir = true\135.txt"
index dc4f462..335a543 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testDynamicWrappingGroupWithReturnValue\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testDynamicWrappingGroupWithReturnValue\133useFir = true\135.txt"
@@ -24,12 +24,13 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = <block>{
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "")
     val tmp4_group = if (x > 0) {
       val tmp3_group = if (%composer.startReplaceableGroup(<>)
       sourceInformation(%composer, "<B()>")
@@ -44,11 +45,12 @@
     } else {
       4
     }
+    %composer.endReplaceableGroup()
     tmp4_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = false\135.txt"
index c008864..af9595d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = false\135.txt"
@@ -26,14 +26,13 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val state = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         mutableStateOf(
           value = false
         )
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val tmp0_subject = state.value
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = true\135.txt"
index c008864..af9595d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = true\135.txt"
@@ -26,14 +26,13 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val state = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         mutableStateOf(
           value = false
         )
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val tmp0_subject = state.value
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testElvis\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testElvis\133useFir = false\135.txt"
index 0910c29..6b3b281 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testElvis\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testElvis\133useFir = false\135.txt"
@@ -23,13 +23,14 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int?, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<R()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val y = <block>{
     val <elvis> = x
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "<R()>")
     val tmp0_group = when {
       <elvis> == null -> {
         R(%composer, 0)
@@ -38,10 +39,11 @@
         <elvis>
       }
     }
+    %composer.endReplaceableGroup()
     tmp0_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testElvis\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testElvis\133useFir = true\135.txt"
index 0910c29..6b3b281 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testElvis\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testElvis\133useFir = true\135.txt"
@@ -23,13 +23,14 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int?, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<R()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val y = <block>{
     val <elvis> = x
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "<R()>")
     val tmp0_group = when {
       <elvis> == null -> {
         R(%composer, 0)
@@ -38,10 +39,11 @@
         <elvis>
       }
     }
+    %composer.endReplaceableGroup()
     tmp0_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBodyAndCallsAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBodyAndCallsAfter\133useFir = false\135.txt"
index 799230f..ac862b1 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBodyAndCallsAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBodyAndCallsAfter\133useFir = false\135.txt"
@@ -25,8 +25,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: List<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -42,5 +41,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBodyAndCallsAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBodyAndCallsAfter\133useFir = true\135.txt"
index 799230f..ac862b1 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBodyAndCallsAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBodyAndCallsAfter\133useFir = true\135.txt"
@@ -25,8 +25,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: List<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -42,5 +41,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBody\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBody\133useFir = false\135.txt"
index 73b254f..4155895 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBody\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBody\133useFir = false\135.txt"
@@ -25,18 +25,20 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: List<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<P(i)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<P(i)>")
   val <iterator> = items.iterator()
   while (<iterator>.hasNext()) {
     val i = <iterator>.next()
     P(i, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBody\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBody\133useFir = true\135.txt"
index 73b254f..4155895 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBody\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInBody\133useFir = true\135.txt"
@@ -25,18 +25,20 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: List<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<P(i)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<P(i)>")
   val <iterator> = items.iterator()
   while (<iterator>.hasNext()) {
     val i = <iterator>.next()
     P(i, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInSubject\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInSubject\133useFir = false\135.txt"
index 4d2696b..4199fcf 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInSubject\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInSubject\133useFir = false\135.txt"
@@ -24,8 +24,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<L()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<L()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -37,5 +36,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInSubject\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInSubject\133useFir = true\135.txt"
index 4d2696b..4199fcf 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInSubject\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testForLoopWithCallsInSubject\133useFir = true\135.txt"
@@ -24,8 +24,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<L()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<L()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -37,5 +36,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = false\135.txt"
index 98027c1..359608f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = false\135.txt"
@@ -35,12 +35,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val a = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         A()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val <iterator> = start until end.iterator()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = true\135.txt"
index 98027c1..359608f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = true\135.txt"
@@ -35,12 +35,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val a = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         A()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val <iterator> = start until end.iterator()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupsInLoops\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupsInLoops\133useFir = false\135.txt"
index c4bbba2..70e51b9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupsInLoops\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupsInLoops\133useFir = false\135.txt"
@@ -53,12 +53,11 @@
         %composer.startMovableGroup(<>, item)
         sourceInformation(%composer, "<rememb...>")
         val tmp0 = <block>{
-          %composer.startReplaceableGroup(<>)
-          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+          sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
           val tmp1_group = %composer.cache(false) {
             item
           }
-          %composer.endReplaceableGroup()
+          sourceInformationMarkerEnd(%composer)
           tmp1_group
         }
         %composer.endMovableGroup()
@@ -97,12 +96,11 @@
         %composer.startMovableGroup(<>, item)
         sourceInformation(%composer, "<rememb...>")
         val tmp0 = <block>{
-          %composer.startReplaceableGroup(<>)
-          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+          sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
           val tmp1_group = %composer.cache(false) {
             item
           }
-          %composer.endReplaceableGroup()
+          sourceInformationMarkerEnd(%composer)
           tmp1_group
         }
         %composer.endMovableGroup()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupsInLoops\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupsInLoops\133useFir = true\135.txt"
index b19edaf..5fbcf02 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupsInLoops\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupsInLoops\133useFir = true\135.txt"
@@ -52,12 +52,11 @@
       if (item > -1) {
         %composer.startMovableGroup(<>, item)
         sourceInformation(%composer, "<rememb...>")
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         %composer.cache(false) {
           item
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         %composer.endMovableGroup()
       }
       %composer.endReplaceableGroup()
@@ -93,12 +92,11 @@
         %composer.startMovableGroup(<>, item)
         sourceInformation(%composer, "<rememb...>")
         val tmp0 = <block>{
-          %composer.startReplaceableGroup(<>)
-          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+          sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
           val tmp1_group = %composer.cache(false) {
             item
           }
-          %composer.endReplaceableGroup()
+          sourceInformationMarkerEnd(%composer)
           tmp1_group
         }
         %composer.endMovableGroup()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInBranch\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInBranch\133useFir = false\135.txt"
index 2b2e8e1..8b48bc4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInBranch\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInBranch\133useFir = false\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -46,5 +45,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInBranch\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInBranch\133useFir = true\135.txt"
index 2b2e8e1..8b48bc4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInBranch\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInBranch\133useFir = true\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -46,5 +45,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInConditions\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInConditions\133useFir = false\135.txt"
index 6d1bf67..44a9f0a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInConditions\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInConditions\133useFir = false\135.txt"
@@ -31,11 +31,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   if (%composer.startReplaceableGroup(<>)
   sourceInformation(%composer, "<B(a)>")
   val tmp0_group = B(a, %composer, 0)
@@ -51,8 +52,9 @@
   } else {
     NA()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInConditions\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInConditions\133useFir = true\135.txt"
index 6d1bf67..44a9f0a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInConditions\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfElseWithCallsInConditions\133useFir = true\135.txt"
@@ -31,11 +31,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   if (%composer.startReplaceableGroup(<>)
   sourceInformation(%composer, "<B(a)>")
   val tmp0_group = B(a, %composer, 0)
@@ -51,8 +52,9 @@
   } else {
     NA()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfNonComposable\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfNonComposable\133useFir = false\135.txt"
index 714206d1..9343080 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfNonComposable\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfNonComposable\133useFir = false\135.txt"
@@ -23,8 +23,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -34,5 +33,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfNonComposable\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfNonComposable\133useFir = true\135.txt"
index 714206d1..9343080 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfNonComposable\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfNonComposable\133useFir = true\135.txt"
@@ -23,8 +23,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -34,5 +33,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallInCondition\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallInCondition\133useFir = false\135.txt"
index 348c308..afa4db1 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallInCondition\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallInCondition\133useFir = false\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<B()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<B()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -40,5 +39,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallInCondition\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallInCondition\133useFir = true\135.txt"
index 348c308..afa4db1 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallInCondition\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallInCondition\133useFir = true\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<B()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<B()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -40,5 +39,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallsInBranch\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallsInBranch\133useFir = false\135.txt"
index e1fc89d..4673898 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallsInBranch\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallsInBranch\133useFir = false\135.txt"
@@ -24,16 +24,18 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A()>")
   if (x > 0) {
     A(%composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallsInBranch\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallsInBranch\133useFir = true\135.txt"
index e1fc89d..4673898 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallsInBranch\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testIfWithCallsInBranch\133useFir = true\135.txt"
@@ -24,16 +24,18 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A()>")
   if (x > 0) {
     A(%composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = false\135.txt"
index 87cb845..8dbb4b8 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = false\135.txt"
@@ -35,108 +35,99 @@
     }
     Array(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     ByteArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toByte()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     CharArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toChar()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     ShortArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toShort()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     IntArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     LongArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toLong()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     FloatArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toFloat()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     DoubleArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toDouble()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     BooleanArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           false
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = true\135.txt"
index 87cb845..8dbb4b8 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = true\135.txt"
@@ -35,108 +35,99 @@
     }
     Array(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     ByteArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toByte()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     CharArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toChar()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     ShortArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toShort()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     IntArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     LongArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toLong()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     FloatArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toFloat()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     DoubleArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           it.toDouble()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
     }
     BooleanArray(n) { it: Int ->
       val tmp1_return = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           false
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
       tmp1_return
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineLambdaBeforeACall\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineLambdaBeforeACall\133useFir = false\135.txt"
index f603a80..1813dd9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineLambdaBeforeACall\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineLambdaBeforeACall\133useFir = false\135.txt"
@@ -20,8 +20,7 @@
 
 @Composable
 private fun Test(param: String?, %composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<Test("...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<Test("...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -40,6 +39,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineLambdaBeforeACall\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineLambdaBeforeACall\133useFir = true\135.txt"
index f603a80..1813dd9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineLambdaBeforeACall\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineLambdaBeforeACall\133useFir = true\135.txt"
@@ -20,8 +20,7 @@
 
 @Composable
 private fun Test(param: String?, %composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<Test("...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<Test("...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -40,6 +39,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineReturnLabel\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineReturnLabel\133useFir = false\135.txt"
index b0bbb6d..75bd69e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineReturnLabel\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineReturnLabel\133useFir = false\135.txt"
@@ -30,8 +30,7 @@
 @Composable
 @NonRestartableComposable
 fun CustomTextBroken(condition: Boolean, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(CustomTextBroken)<FakeBo...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(CustomTextBroken)<FakeBo...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -48,13 +47,12 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @Composable
 @ComposableInferredTarget(scheme = "[0[0]]")
 fun FakeBox(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(FakeBox)<conten...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "CC(FakeBox)<conten...>:Test.kt")
   content(%composer, 0b1110 and %changed)
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineReturnLabel\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineReturnLabel\133useFir = true\135.txt"
index b0bbb6d..75bd69e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineReturnLabel\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineReturnLabel\133useFir = true\135.txt"
@@ -30,8 +30,7 @@
 @Composable
 @NonRestartableComposable
 fun CustomTextBroken(condition: Boolean, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(CustomTextBroken)<FakeBo...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(CustomTextBroken)<FakeBo...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -48,13 +47,12 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @Composable
 @ComposableInferredTarget(scheme = "[0[0]]")
 fun FakeBox(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(FakeBox)<conten...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "CC(FakeBox)<conten...>:Test.kt")
   content(%composer, 0b1110 and %changed)
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAsAValue\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAsAValue\133useFir = false\135.txt"
index b83cafc..ef3f93e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAsAValue\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAsAValue\133useFir = false\135.txt"
@@ -21,8 +21,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<P(y)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<P(y)>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -37,5 +36,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAsAValue\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAsAValue\133useFir = true\135.txt"
index b83cafc..ef3f93e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAsAValue\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAsAValue\133useFir = true\135.txt"
@@ -21,8 +21,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<P(y)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<P(y)>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -37,5 +36,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsAfter\133useFir = false\135.txt"
index 572fe15..b49e490 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsAfter\133useFir = false\135.txt"
@@ -23,8 +23,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A(b)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A(b)>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -36,5 +35,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsAfter\133useFir = true\135.txt"
index 572fe15..b49e490 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsAfter\133useFir = true\135.txt"
@@ -23,8 +23,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A(b)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A(b)>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -36,5 +35,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsBefore\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsBefore\133useFir = false\135.txt"
index 7df902a..076293f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsBefore\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsBefore\133useFir = false\135.txt"
@@ -23,8 +23,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A(a)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A(a)>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -36,5 +35,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsBefore\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsBefore\133useFir = true\135.txt"
index 7df902a..076293f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsBefore\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevelAndCallsBefore\133useFir = true\135.txt"
@@ -23,8 +23,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A(a)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A(a)>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -36,5 +35,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevel\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevel\133useFir = false\135.txt"
index 849779e..e29ec6d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevel\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevel\133useFir = false\135.txt"
@@ -22,8 +22,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -34,5 +33,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevel\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevel\133useFir = true\135.txt"
index 849779e..e29ec6d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevel\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyAtRootLevel\133useFir = true\135.txt"
@@ -22,8 +22,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -34,5 +33,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsAfter\133useFir = false\135.txt"
index c1aac5b..5c95f9f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsAfter\133useFir = false\135.txt"
@@ -25,11 +25,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A(b)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A(b)>")
   if (x > 0) {
     %composer.startMovableGroup(<>, x)
     sourceInformation(%composer, "<A(a)>")
@@ -37,8 +38,9 @@
     %composer.endMovableGroup()
     A(b, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsAfter\133useFir = true\135.txt"
index c1aac5b..5c95f9f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsAfter\133useFir = true\135.txt"
@@ -25,11 +25,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A(b)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A(b)>")
   if (x > 0) {
     %composer.startMovableGroup(<>, x)
     sourceInformation(%composer, "<A(a)>")
@@ -37,8 +38,9 @@
     %composer.endMovableGroup()
     A(b, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsBefore\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsBefore\133useFir = false\135.txt"
index ed1aac1..4152631e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsBefore\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsBefore\133useFir = false\135.txt"
@@ -25,11 +25,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A(a)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A(a)>")
   if (x > 0) {
     A(a, %composer, 0)
     %composer.startMovableGroup(<>, x)
@@ -37,8 +38,9 @@
     A(b, %composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsBefore\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsBefore\133useFir = true\135.txt"
index ed1aac1..4152631e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsBefore\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIfAndCallsBefore\133useFir = true\135.txt"
@@ -25,11 +25,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A(a)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A(a)>")
   if (x > 0) {
     A(a, %composer, 0)
     %composer.startMovableGroup(<>, x)
@@ -37,8 +38,9 @@
     A(b, %composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIf\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIf\133useFir = false\135.txt"
index 9e54adf..03e5b2f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIf\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIf\133useFir = false\135.txt"
@@ -24,19 +24,21 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   if (x > 0) {
     %composer.startMovableGroup(<>, x)
     sourceInformation(%composer, "<A()>")
     A(%composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIf\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIf\133useFir = true\135.txt"
index 9e54adf..03e5b2f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIf\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyInIf\133useFir = true\135.txt"
@@ -24,19 +24,21 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   if (x > 0) {
     %composer.startMovableGroup(<>, x)
     sourceInformation(%composer, "<A()>")
     A(%composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithComposableValue\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithComposableValue\133useFir = false\135.txt"
index 7cf8568..91509c0 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithComposableValue\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithComposableValue\133useFir = false\135.txt"
@@ -24,19 +24,21 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<R()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<R()>")
   while (x > 0) {
     %composer.startMovableGroup(<>, R(%composer, 0))
     sourceInformation(%composer, "<A()>")
     A(%composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithComposableValue\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithComposableValue\133useFir = true\135.txt"
index 7cf8568..91509c0 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithComposableValue\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithComposableValue\133useFir = true\135.txt"
@@ -24,19 +24,21 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<R()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<R()>")
   while (x > 0) {
     %composer.startMovableGroup(<>, R(%composer, 0))
     sourceInformation(%composer, "<A()>")
     A(%composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithLotsOfValues\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithLotsOfValues\133useFir = false\135.txt"
index ffe5ab1..2c62e17 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithLotsOfValues\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithLotsOfValues\133useFir = false\135.txt"
@@ -22,8 +22,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(a: Int, b: Int, c: Int, d: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -34,5 +33,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithLotsOfValues\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithLotsOfValues\133useFir = true\135.txt"
index ffe5ab1..2c62e17 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithLotsOfValues\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testKeyWithLotsOfValues\133useFir = true\135.txt"
@@ -22,8 +22,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(a: Int, b: Int, c: Int, d: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -34,5 +33,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLambdaWithNonUnitResult\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLambdaWithNonUnitResult\133useFir = false\135.txt"
new file mode 100644
index 0000000..65deee7
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLambdaWithNonUnitResult\133useFir = false\135.txt"
@@ -0,0 +1,49 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.*
+
+@Composable
+fun Test() {
+    val factory = createFactory {
+        10
+    }
+    factory()
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@Composable
+fun Test(%composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Test)<factor...>:Test.kt")
+  if (%changed != 0 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %changed, -1, <>)
+    }
+    val factory = createFactory { %composer: Composer?, %changed: Int ->
+      %composer.startReplaceableGroup(<>)
+      if (isTraceInProgress()) {
+        traceEventStart(<>, %changed, -1, <>)
+      }
+      val tmp0 = 10
+      if (isTraceInProgress()) {
+        traceEventEnd()
+      }
+      %composer.endReplaceableGroup()
+      tmp0
+    }
+    factory(%composer, 0)
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Test(%composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLambdaWithNonUnitResult\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLambdaWithNonUnitResult\133useFir = true\135.txt"
new file mode 100644
index 0000000..65deee7
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLambdaWithNonUnitResult\133useFir = true\135.txt"
@@ -0,0 +1,49 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.*
+
+@Composable
+fun Test() {
+    val factory = createFactory {
+        10
+    }
+    factory()
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@Composable
+fun Test(%composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Test)<factor...>:Test.kt")
+  if (%changed != 0 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %changed, -1, <>)
+    }
+    val factory = createFactory { %composer: Composer?, %changed: Int ->
+      %composer.startReplaceableGroup(<>)
+      if (isTraceInProgress()) {
+        traceEventStart(<>, %changed, -1, <>)
+      }
+      val tmp0 = 10
+      if (isTraceInProgress()) {
+        traceEventEnd()
+      }
+      %composer.endReplaceableGroup()
+      tmp0
+    }
+    factory(%composer, 0)
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Test(%composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLoopWithBreak\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLoopWithBreak\133useFir = false\135.txt"
index 46ab04c..be60c3e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLoopWithBreak\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLoopWithBreak\133useFir = false\135.txt"
@@ -30,11 +30,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A()>")
   a@while (a.hasNext()) {
     val x = a.next()
     %composer.startReplaceableGroup(<>)
@@ -50,8 +51,9 @@
     %composer.endReplaceableGroup()
     A(%composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLoopWithBreak\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLoopWithBreak\133useFir = true\135.txt"
index 46ab04c..be60c3e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLoopWithBreak\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testLoopWithBreak\133useFir = true\135.txt"
@@ -30,11 +30,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A()>")
   a@while (a.hasNext()) {
     val x = a.next()
     %composer.startReplaceableGroup(<>)
@@ -50,8 +51,9 @@
     %composer.endReplaceableGroup()
     A(%composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testNestedLoops\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testNestedLoops\133useFir = false\135.txt"
index 485b249..7cedd68 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testNestedLoops\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testNestedLoops\133useFir = false\135.txt"
@@ -26,8 +26,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -47,5 +46,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testNestedLoops\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testNestedLoops\133useFir = true\135.txt"
index 485b249..7cedd68 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testNestedLoops\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testNestedLoops\133useFir = true\135.txt"
@@ -26,8 +26,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -47,5 +46,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOR\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOR\133useFir = false\135.txt"
index 0cdc751..0ee3819 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOR\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOR\133useFir = false\135.txt"
@@ -21,15 +21,17 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<B()>,<B()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<B()>,<B()>")
   val tmp0_group = B(%composer, 0) || B(%composer, 0)
+  %composer.endReplaceableGroup()
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOR\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOR\133useFir = true\135.txt"
index 0cdc751..0ee3819 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOR\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOR\133useFir = true\135.txt"
@@ -21,15 +21,17 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<B()>,<B()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<B()>,<B()>")
   val tmp0_group = B(%composer, 0) || B(%composer, 0)
+  %composer.endReplaceableGroup()
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOverrideWithNonUnitResult\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOverrideWithNonUnitResult\133useFir = false\135.txt"
new file mode 100644
index 0000000..2458288
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOverrideWithNonUnitResult\133useFir = false\135.txt"
@@ -0,0 +1,33 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.*
+
+class SomeClassImpl: SomeClass() {
+    @Composable
+    override fun SomeFunction(): Int = 10
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@StabilityInferred(parameters = 1)
+class SomeClassImpl : SomeClass {
+  @Composable
+  override fun SomeFunction(%composer: Composer?, %changed: Int): Int {
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "C(SomeFunction):Test.kt")
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %changed, -1, <>)
+    }
+    val tmp0 = 10
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+    %composer.endReplaceableGroup()
+    return tmp0
+  }
+  static val %stable: Int = 0
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOverrideWithNonUnitResult\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOverrideWithNonUnitResult\133useFir = true\135.txt"
new file mode 100644
index 0000000..2458288
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testOverrideWithNonUnitResult\133useFir = true\135.txt"
@@ -0,0 +1,33 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.*
+
+class SomeClassImpl: SomeClass() {
+    @Composable
+    override fun SomeFunction(): Int = 10
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@StabilityInferred(parameters = 1)
+class SomeClassImpl : SomeClass {
+  @Composable
+  override fun SomeFunction(%composer: Composer?, %changed: Int): Int {
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "C(SomeFunction):Test.kt")
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %changed, -1, <>)
+    }
+    val tmp0 = 10
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+    %composer.endReplaceableGroup()
+    return tmp0
+  }
+  static val %stable: Int = 0
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = false\135.txt"
index 36ea817..31ea2f2 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = false\135.txt"
@@ -35,12 +35,11 @@
       %composer.startReplaceableGroup(<>)
       sourceInformation(%composer, "<rememb...>")
       val tmp1_group = if (param == null) {
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           ""
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       } else {
         null
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = true\135.txt"
index 36ea817..31ea2f2 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = true\135.txt"
@@ -35,12 +35,11 @@
       %composer.startReplaceableGroup(<>)
       sourceInformation(%composer, "<rememb...>")
       val tmp1_group = if (param == null) {
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           ""
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       } else {
         null
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = false\135.txt"
index b3bbae4..4d48d88 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = false\135.txt"
@@ -27,8 +27,7 @@
 
 @Composable
 private fun Test(param: String?, %composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<Test(>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<Test(>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -40,12 +39,11 @@
         %composer.startReplaceableGroup(<>)
         sourceInformation(%composer, "<rememb...>")
         val tmp2_group = if (param == null) {
-          %composer.startReplaceableGroup(<>)
-          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+          sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
           val tmp1_group = %composer.cache(false) {
             ""
           }
-          %composer.endReplaceableGroup()
+          sourceInformationMarkerEnd(%composer)
           tmp1_group
         } else {
           null
@@ -62,6 +60,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = true\135.txt"
index b3bbae4..4d48d88 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = true\135.txt"
@@ -27,8 +27,7 @@
 
 @Composable
 private fun Test(param: String?, %composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<Test(>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<Test(>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -40,12 +39,11 @@
         %composer.startReplaceableGroup(<>)
         sourceInformation(%composer, "<rememb...>")
         val tmp2_group = if (param == null) {
-          %composer.startReplaceableGroup(<>)
-          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+          sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
           val tmp1_group = %composer.cache(false) {
             ""
           }
-          %composer.endReplaceableGroup()
+          sourceInformationMarkerEnd(%composer)
           tmp1_group
         } else {
           null
@@ -62,6 +60,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnCallValue\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnCallValue\133useFir = false\135.txt"
index 5fe2a78..b1ee204 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnCallValue\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnCallValue\133useFir = false\135.txt"
@@ -23,8 +23,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>,<R()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>,<R()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -33,6 +32,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnCallValue\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnCallValue\133useFir = true\135.txt"
index 5fe2a78..b1ee204 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnCallValue\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnCallValue\133useFir = true\135.txt"
@@ -23,8 +23,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>,<R()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>,<R()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -33,6 +32,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnInlinedExpressionWithCall\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnInlinedExpressionWithCall\133useFir = false\135.txt"
index 4baf241..ed747d1 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnInlinedExpressionWithCall\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnInlinedExpressionWithCall\133useFir = false\135.txt"
@@ -26,22 +26,24 @@
 
 @Composable
 fun Test(x: Int, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)*<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = <block>{
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "*<A()>")
     val tmp1_group = x.let { it: Int ->
       A(%composer, 0)
       val tmp0_return = 123
       tmp0_return
     }
+    %composer.endReplaceableGroup()
     tmp1_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnInlinedExpressionWithCall\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnInlinedExpressionWithCall\133useFir = true\135.txt"
index 4baf241..ed747d1 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnInlinedExpressionWithCall\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnInlinedExpressionWithCall\133useFir = true\135.txt"
@@ -26,22 +26,24 @@
 
 @Composable
 fun Test(x: Int, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)*<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = <block>{
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "*<A()>")
     val tmp1_group = x.let { it: Int ->
       A(%composer, 0)
       val tmp0_return = 123
       tmp0_return
     }
+    %composer.endReplaceableGroup()
     tmp1_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnNull\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnNull\133useFir = false\135.txt"
index 537592c..406738b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnNull\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnNull\133useFir = false\135.txt"
@@ -69,8 +69,7 @@
 
 @Composable
 fun Test(%composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -78,7 +77,7 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
@@ -132,8 +131,7 @@
 }
 @Composable
 fun Test4(b: Boolean, %composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test4):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test4):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -145,13 +143,12 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun Test5(%composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test5):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test5):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -160,13 +157,12 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun Test6(%composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test6):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test6):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -174,7 +170,7 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @Composable
 fun Test7(b: Boolean, %composer: Composer?, %changed: Int): String? {
@@ -200,8 +196,7 @@
 }
 @Composable
 fun Test8(%composer: Composer?, %changed: Int): Unit? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test8)<Test6(...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test8)<Test6(...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -211,13 +206,12 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun Test9(%composer: Composer?, %changed: Int): Unit? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test9)<Test6(...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test9)<Test6(...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -227,13 +221,12 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun Test10(%composer: Composer?, %changed: Int): Unit? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test10)<Test6(...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test10)<Test6(...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -242,6 +235,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnNull\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnNull\133useFir = true\135.txt"
index d205974..2ef96cd 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnNull\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testReturnNull\133useFir = true\135.txt"
@@ -69,8 +69,7 @@
 
 @Composable
 fun Test(%composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -78,7 +77,7 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
@@ -132,8 +131,7 @@
 }
 @Composable
 fun Test4(b: Boolean, %composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test4):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test4):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -141,13 +139,12 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun Test5(%composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test5):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test5):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -156,13 +153,12 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun Test6(%composer: Composer?, %changed: Int): String? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test6):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test6):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -170,7 +166,7 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @Composable
 fun Test7(b: Boolean, %composer: Composer?, %changed: Int): String? {
@@ -196,8 +192,7 @@
 }
 @Composable
 fun Test8(%composer: Composer?, %changed: Int): Unit? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test8)<Test6(...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test8)<Test6(...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -207,13 +202,12 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun Test9(%composer: Composer?, %changed: Int): Unit? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test9)<Test6(...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test9)<Test6(...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -223,13 +217,12 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun Test10(%composer: Composer?, %changed: Int): Unit? {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test10)<Test6(...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test10)<Test6(...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -238,6 +231,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testSafeCall\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testSafeCall\133useFir = false\135.txt"
index 03e0ce1..33b11fc2 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testSafeCall\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testSafeCall\133useFir = false\135.txt"
@@ -23,14 +23,25 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int?, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  x?.A(%composer, 0b1110 and %changed)
+  val tmp0_safe_receiver = x
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A()>")
+  val tmp0_group = when {
+    tmp0_safe_receiver == null -> {
+      null
+    }
+    else -> {
+      tmp0_safe_receiver.A(%composer, 0b1110 and %changed)
+    }
+  }
+  %composer.endReplaceableGroup()
+  tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testSafeCall\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testSafeCall\133useFir = true\135.txt"
index 03e0ce1..33b11fc2 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testSafeCall\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testSafeCall\133useFir = true\135.txt"
@@ -23,14 +23,25 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int?, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  x?.A(%composer, 0b1110 and %changed)
+  val tmp0_safe_receiver = x
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A()>")
+  val tmp0_group = when {
+    tmp0_safe_receiver == null -> {
+      null
+    }
+    else -> {
+      tmp0_safe_receiver.A(%composer, 0b1110 and %changed)
+    }
+  }
+  %composer.endReplaceableGroup()
+  tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testTheThing\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testTheThing\133useFir = false\135.txt"
index 6627f87..619a9f7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testTheThing\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testTheThing\133useFir = false\135.txt"
@@ -57,8 +57,7 @@
 @NonRestartableComposable
 @Composable
 fun Simple(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Simple)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Simple)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -72,7 +71,7 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @NonRestartableComposable
 @Composable
@@ -103,8 +102,7 @@
 @NonRestartableComposable
 @Composable
 fun NoCalls(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(NoCalls)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(NoCalls)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -115,21 +113,23 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @NonRestartableComposable
 @Composable
 fun NoCallsAfter(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(NoCallsAfter)*<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(NoCallsAfter):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A()>")
   run {
     A(%composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testTheThing\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testTheThing\133useFir = true\135.txt"
index 6627f87..619a9f7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testTheThing\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testTheThing\133useFir = true\135.txt"
@@ -57,8 +57,7 @@
 @NonRestartableComposable
 @Composable
 fun Simple(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Simple)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Simple)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -72,7 +71,7 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @NonRestartableComposable
 @Composable
@@ -103,8 +102,7 @@
 @NonRestartableComposable
 @Composable
 fun NoCalls(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(NoCalls)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(NoCalls)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -115,21 +113,23 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @NonRestartableComposable
 @Composable
 fun NoCallsAfter(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(NoCallsAfter)*<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(NoCallsAfter):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A()>")
   run {
     A(%composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditionsAndCallAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditionsAndCallAfter\133useFir = false\135.txt"
index 29ae4df..9681ed7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditionsAndCallAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditionsAndCallAfter\133useFir = false\135.txt"
@@ -28,8 +28,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -59,5 +58,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditionsAndCallAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditionsAndCallAfter\133useFir = true\135.txt"
index 29ae4df..9681ed7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditionsAndCallAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditionsAndCallAfter\133useFir = true\135.txt"
@@ -28,8 +28,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -59,5 +58,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditions\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditions\133useFir = false\135.txt"
index e1c01ea..2458736 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditions\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditions\133useFir = false\135.txt"
@@ -28,11 +28,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   when {
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "<R(a)>")
@@ -52,8 +53,9 @@
       NA()
     }
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditions\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditions\133useFir = true\135.txt"
index e1c01ea..2458736 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditions\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInConditions\133useFir = true\135.txt"
@@ -28,11 +28,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   when {
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "<R(a)>")
@@ -52,8 +53,9 @@
       NA()
     }
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInSomeResults\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInSomeResults\133useFir = false\135.txt"
index 26d3c7b..2cee4f4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInSomeResults\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInSomeResults\133useFir = false\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -54,5 +53,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInSomeResults\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInSomeResults\133useFir = true\135.txt"
index 26d3c7b..2cee4f4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInSomeResults\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCallsInSomeResults\133useFir = true\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -54,5 +53,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCalls\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCalls\133useFir = false\135.txt"
index b5656eb..6951232 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCalls\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCalls\133useFir = false\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -55,5 +54,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCalls\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCalls\133useFir = true\135.txt"
index b5656eb..6951232 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCalls\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithCalls\133useFir = true\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -55,5 +54,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCallsWithResult\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCallsWithResult\133useFir = false\135.txt"
index 82c50da..ec19619 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCallsWithResult\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCallsWithResult\133useFir = false\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -61,5 +60,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCallsWithResult\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCallsWithResult\133useFir = true\135.txt"
index 82c50da..ec19619 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCallsWithResult\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCallsWithResult\133useFir = true\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -61,5 +60,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCalls\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCalls\133useFir = false\135.txt"
index 68d1ac4..bad6af4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCalls\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCalls\133useFir = false\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -56,5 +55,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCalls\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCalls\133useFir = true\135.txt"
index 68d1ac4..bad6af4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCalls\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndCalls\133useFir = true\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -56,5 +55,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndNoCalls\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndNoCalls\133useFir = false\135.txt"
index bf646b0..100ac6c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndNoCalls\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndNoCalls\133useFir = false\135.txt"
@@ -25,8 +25,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -45,5 +44,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndNoCalls\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndNoCalls\133useFir = true\135.txt"
index bf646b0..100ac6c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndNoCalls\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhenWithSubjectAndNoCalls\133useFir = true\135.txt"
@@ -25,8 +25,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -45,5 +44,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallAfter\133useFir = false\135.txt"
index 5bd1970..b15c231 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallAfter\133useFir = false\135.txt"
@@ -25,11 +25,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A()>")
   if (x > 0) {
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "*<A()>")
@@ -39,8 +40,9 @@
     %composer.endReplaceableGroup()
     A(%composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallAfter\133useFir = true\135.txt"
index 5bd1970..b15c231 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallAfter\133useFir = true\135.txt"
@@ -25,11 +25,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A()>")
   if (x > 0) {
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "*<A()>")
@@ -39,8 +40,9 @@
     %composer.endReplaceableGroup()
     A(%composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallBefore\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallBefore\133useFir = false\135.txt"
index 3479e9e..69b1c1a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallBefore\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallBefore\133useFir = false\135.txt"
@@ -25,19 +25,21 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>,*<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A()>,*<A()>")
   if (x > 0) {
     A(%composer, 0)
     while (x > 0) {
       A(%composer, 0)
     }
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallBefore\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallBefore\133useFir = true\135.txt"
index 3479e9e..69b1c1a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallBefore\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIfAndCallBefore\133useFir = true\135.txt"
@@ -25,19 +25,21 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>,*<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "<A()>,*<A()>")
   if (x > 0) {
     A(%composer, 0)
     while (x > 0) {
       A(%composer, 0)
     }
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIf\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIf\133useFir = false\135.txt"
index 6c9106d..07965c2 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIf\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIf\133useFir = false\135.txt"
@@ -24,18 +24,20 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A()>")
   if (x > 0) {
     while (x > 0) {
       A(%composer, 0)
     }
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIf\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIf\133useFir = true\135.txt"
index 6c9106d..07965c2 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIf\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileInsideIf\133useFir = true\135.txt"
@@ -24,18 +24,20 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A()>")
   if (x > 0) {
     while (x > 0) {
       A(%composer, 0)
     }
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBodyAndCallsAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBodyAndCallsAfter\133useFir = false\135.txt"
index b1bac84..d5bd839 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBodyAndCallsAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBodyAndCallsAfter\133useFir = false\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: MutableList<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -43,5 +42,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBodyAndCallsAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBodyAndCallsAfter\133useFir = true\135.txt"
index b1bac84..d5bd839 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBodyAndCallsAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBodyAndCallsAfter\133useFir = true\135.txt"
@@ -27,8 +27,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: MutableList<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -43,5 +42,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBody\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBody\133useFir = false\135.txt"
index 403cb64..96a283c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBody\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBody\133useFir = false\135.txt"
@@ -27,17 +27,19 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: MutableList<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<P(item...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<P(item...>")
   while (items.isNotEmpty()) {
     val item = items.removeAt(items.size - 1)
     P(item, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBody\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBody\133useFir = true\135.txt"
index 403cb64..96a283c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBody\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInBody\133useFir = true\135.txt"
@@ -27,17 +27,19 @@
 @NonRestartableComposable
 @Composable
 fun Example(items: MutableList<Int>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<P(item...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<P(item...>")
   while (items.isNotEmpty()) {
     val item = items.removeAt(items.size - 1)
     P(item, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBodyAndCallsAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBodyAndCallsAfter\133useFir = false\135.txt"
index 4ac791b..6d83f67 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBodyAndCallsAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBodyAndCallsAfter\133useFir = false\135.txt"
@@ -26,8 +26,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A(b)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A(b)>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -41,5 +40,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBodyAndCallsAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBodyAndCallsAfter\133useFir = true\135.txt"
index 4ac791b..6d83f67 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBodyAndCallsAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBodyAndCallsAfter\133useFir = true\135.txt"
@@ -26,8 +26,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A(b)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A(b)>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -41,5 +40,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBody\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBody\133useFir = false\135.txt"
index 697f903..eb47a3d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBody\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBody\133useFir = false\135.txt"
@@ -25,16 +25,18 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<B()>,<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<B()>,<A()>")
   while (B(%composer, 0)) {
     A(%composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBody\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBody\133useFir = true\135.txt"
index 697f903..eb47a3d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBody\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndBody\133useFir = true\135.txt"
@@ -25,16 +25,18 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<B()>,<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<B()>,<A()>")
   while (B(%composer, 0)) {
     A(%composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndCallsAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndCallsAfter\133useFir = false\135.txt"
index 6c93a81..836920e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndCallsAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndCallsAfter\133useFir = false\135.txt"
@@ -25,8 +25,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -40,5 +39,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndCallsAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndCallsAfter\133useFir = true\135.txt"
index 6c93a81..836920e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndCallsAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInConditionAndCallsAfter\133useFir = true\135.txt"
@@ -25,8 +25,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -40,5 +39,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInCondition\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInCondition\133useFir = false\135.txt"
index 0c8889e..c84f9e7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInCondition\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInCondition\133useFir = false\135.txt"
@@ -25,16 +25,18 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<B()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<B()>")
   while (B(%composer, 0)) {
     print("hello world")
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInCondition\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInCondition\133useFir = true\135.txt"
index 0c8889e..c84f9e7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInCondition\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileLoopWithCallsInCondition\133useFir = true\135.txt"
@@ -25,16 +25,18 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<B()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<B()>")
   while (B(%composer, 0)) {
     print("hello world")
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallAfter\133useFir = false\135.txt"
index 40c58df..822c943 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallAfter\133useFir = false\135.txt"
@@ -25,11 +25,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<A(b)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A(b)>")
   while (x > 0) {
     %composer.startMovableGroup(<>, x)
     sourceInformation(%composer, "<A(a)>")
@@ -37,8 +38,9 @@
     %composer.endMovableGroup()
     A(b, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallAfter\133useFir = true\135.txt"
index 40c58df..822c943 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallAfter\133useFir = true\135.txt"
@@ -25,11 +25,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<A(b)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A(b)>")
   while (x > 0) {
     %composer.startMovableGroup(<>, x)
     sourceInformation(%composer, "<A(a)>")
@@ -37,8 +38,9 @@
     %composer.endMovableGroup()
     A(b, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBeforeAndAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBeforeAndAfter\133useFir = false\135.txt"
index d91a877..8feac5a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBeforeAndAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBeforeAndAfter\133useFir = false\135.txt"
@@ -26,11 +26,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<A(a)>,<A(c)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A(a)>,<A(c)>")
   while (x > 0) {
     A(a, %composer, 0)
     %composer.startMovableGroup(<>, x)
@@ -39,8 +40,9 @@
     %composer.endMovableGroup()
     A(c, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBeforeAndAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBeforeAndAfter\133useFir = true\135.txt"
index d91a877..8feac5a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBeforeAndAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBeforeAndAfter\133useFir = true\135.txt"
@@ -26,11 +26,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<A(a)>,<A(c)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A(a)>,<A(c)>")
   while (x > 0) {
     A(a, %composer, 0)
     %composer.startMovableGroup(<>, x)
@@ -39,8 +40,9 @@
     %composer.endMovableGroup()
     A(c, %composer, 0)
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBefore\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBefore\133useFir = false\135.txt"
index d181f75..d2dae96 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBefore\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBefore\133useFir = false\135.txt"
@@ -25,11 +25,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<A(a)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A(a)>")
   while (x > 0) {
     A(a, %composer, 0)
     %composer.startMovableGroup(<>, x)
@@ -37,8 +38,9 @@
     A(b, %composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBefore\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBefore\133useFir = true\135.txt"
index d181f75..d2dae96 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBefore\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKeyAndCallBefore\133useFir = true\135.txt"
@@ -25,11 +25,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)*<A(a)>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "*<A(a)>")
   while (x > 0) {
     A(a, %composer, 0)
     %composer.startMovableGroup(<>, x)
@@ -37,8 +38,9 @@
     A(b, %composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKey\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKey\133useFir = false\135.txt"
index 523348e..6aa8167 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKey\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKey\133useFir = false\135.txt"
@@ -24,19 +24,21 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   while (x > 0) {
     %composer.startMovableGroup(<>, x)
     sourceInformation(%composer, "<A()>")
     A(%composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKey\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKey\133useFir = true\135.txt"
index 523348e..6aa8167 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKey\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithKey\133useFir = true\135.txt"
@@ -24,19 +24,21 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   while (x > 0) {
     %composer.startMovableGroup(<>, x)
     sourceInformation(%composer, "<A()>")
     A(%composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithTwoKeys\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithTwoKeys\133useFir = false\135.txt"
index 1fcd79d..938e6cf 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithTwoKeys\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithTwoKeys\133useFir = false\135.txt"
@@ -27,11 +27,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   while (x > 0) {
     %composer.startMovableGroup(<>, x)
     sourceInformation(%composer, "<A(a)>")
@@ -42,8 +43,9 @@
     A(b, %composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithTwoKeys\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithTwoKeys\133useFir = true\135.txt"
index 1fcd79d..938e6cf 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithTwoKeys\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testWhileWithTwoKeys\133useFir = true\135.txt"
@@ -27,11 +27,12 @@
 @NonRestartableComposable
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   while (x > 0) {
     %composer.startMovableGroup(<>, x)
     sourceInformation(%composer, "<A(a)>")
@@ -42,8 +43,9 @@
     A(b, %composer, 0)
     %composer.endMovableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = false\135.txt"
index f7afbd3..e3171f7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = false\135.txt"
@@ -27,8 +27,7 @@
 @Composable
 @NonRestartableComposable
 fun Test(condition: Boolean, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<Text("...>,<Inline...>,<Text("...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<Text("...>,<Inline...>,<Text("...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -56,5 +55,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = true\135.txt"
index f7afbd3..e3171f7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = true\135.txt"
@@ -27,8 +27,7 @@
 @Composable
 @NonRestartableComposable
 fun Test(condition: Boolean, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<Text("...>,<Inline...>,<Text("...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<Text("...>,<Inline...>,<Text("...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -56,5 +55,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromNestedInlineFunction\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromNestedInlineFunction\133useFir = false\135.txt"
index fdeba0f..c804418 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromNestedInlineFunction\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromNestedInlineFunction\133useFir = false\135.txt"
@@ -27,8 +27,7 @@
 @Composable
 @NonRestartableComposable
 fun Test(condition: Boolean, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<Text("...>,<Inline...>,<Text("...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<Text("...>,<Inline...>,<Text("...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -54,5 +53,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromNestedInlineFunction\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromNestedInlineFunction\133useFir = true\135.txt"
index fdeba0f..c804418 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromNestedInlineFunction\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/verifyEarlyExitFromNestedInlineFunction\133useFir = true\135.txt"
@@ -27,8 +27,7 @@
 @Composable
 @NonRestartableComposable
 fun Test(condition: Boolean, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<Text("...>,<Inline...>,<Text("...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<Text("...>,<Inline...>,<Text("...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -54,5 +53,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTestsNoSource/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTestsNoSource/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = false\135.txt"
index 8094b47..68a2b0f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTestsNoSource/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTestsNoSource/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = false\135.txt"
@@ -27,7 +27,6 @@
 @Composable
 @NonRestartableComposable
 fun Test(condition: Boolean, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
   Text("Before outer", %composer, 0b0110)
   InlineLinearA({ %composer: Composer?, %changed: Int ->
     val tmp0_marker = %composer.currentMarker
@@ -47,5 +46,4 @@
     %composer.endReplaceableGroup()
   }, %composer, 0)
   Text("Before outer", %composer, 0b0110)
-  %composer.endReplaceableGroup()
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTestsNoSource/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTestsNoSource/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = true\135.txt"
index 8094b47..68a2b0f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTestsNoSource/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTestsNoSource/verifyEarlyExitFromMultiLevelNestedInlineFunction\133useFir = true\135.txt"
@@ -27,7 +27,6 @@
 @Composable
 @NonRestartableComposable
 fun Test(condition: Boolean, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
   Text("Before outer", %composer, 0b0110)
   InlineLinearA({ %composer: Composer?, %changed: Int ->
     val tmp0_marker = %composer.currentMarker
@@ -47,5 +46,4 @@
     %composer.endReplaceableGroup()
   }, %composer, 0)
   Text("Before outer", %composer, 0b0110)
-  %composer.endReplaceableGroup()
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsForFakeOverridesSuperMethods\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsForFakeOverridesSuperMethods\133useFir = false\135.txt"
index 36a8281..7fe2adb 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsForFakeOverridesSuperMethods\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsForFakeOverridesSuperMethods\133useFir = false\135.txt"
@@ -24,8 +24,7 @@
   @NonRestartableComposable
   @Composable
   fun foo(x: Int, %composer: Composer?, %changed: Int, %default: Int) {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C(foo):Test.kt")
+    sourceInformationMarkerStart(%composer, <>, "C(foo):Test.kt")
     if (%default and 0b0001 != 0) {
       x = 0
     }
@@ -35,7 +34,7 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
   }
   static val %stable: Int = 0
 }
@@ -44,8 +43,7 @@
   @NonRestartableComposable
   @Composable
   fun Example(%composer: Composer?, %changed: Int) {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C(Example)<foo()>:Test.kt")
+    sourceInformationMarkerStart(%composer, <>, "C(Example)<foo()>:Test.kt")
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
@@ -53,7 +51,7 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
   }
   static val %stable: Int = 0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsForFakeOverridesSuperMethods\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsForFakeOverridesSuperMethods\133useFir = true\135.txt"
index 36a8281..7fe2adb 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsForFakeOverridesSuperMethods\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsForFakeOverridesSuperMethods\133useFir = true\135.txt"
@@ -24,8 +24,7 @@
   @NonRestartableComposable
   @Composable
   fun foo(x: Int, %composer: Composer?, %changed: Int, %default: Int) {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C(foo):Test.kt")
+    sourceInformationMarkerStart(%composer, <>, "C(foo):Test.kt")
     if (%default and 0b0001 != 0) {
       x = 0
     }
@@ -35,7 +34,7 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
   }
   static val %stable: Int = 0
 }
@@ -44,8 +43,7 @@
   @NonRestartableComposable
   @Composable
   fun Example(%composer: Composer?, %changed: Int) {
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "C(Example)<foo()>:Test.kt")
+    sourceInformationMarkerStart(%composer, <>, "C(Example)<foo()>:Test.kt")
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
@@ -53,7 +51,7 @@
     if (isTraceInProgress()) {
       traceEventEnd()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
   }
   static val %stable: Int = 0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsOnInvoke\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsOnInvoke\133useFir = false\135.txt"
index 842346e..55e3722 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsOnInvoke\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsOnInvoke\133useFir = false\135.txt"
@@ -21,8 +21,7 @@
 @NonRestartableComposable
 @Composable
 fun Bar(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Bar)<HasDef...>,<NoDefa...>,<Multip...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Bar)<HasDef...>,<NoDefa...>,<Multip...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -32,5 +31,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsOnInvoke\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsOnInvoke\133useFir = true\135.txt"
index 842346e..55e3722 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsOnInvoke\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.DefaultParamTransformTests/testDefaultArgsOnInvoke\133useFir = true\135.txt"
@@ -21,8 +21,7 @@
 @NonRestartableComposable
 @Composable
 fun Bar(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Bar)<HasDef...>,<NoDefa...>,<Multip...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Bar)<HasDef...>,<NoDefa...>,<Multip...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -32,5 +31,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableFunExprBody\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableFunExprBody\133useFir = false\135.txt"
index 67cf6c5..56ab8ec 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableFunExprBody\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableFunExprBody\133useFir = false\135.txt"
@@ -15,8 +15,7 @@
 
 @Composable
 fun Test(x: Int, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -24,6 +23,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableFunExprBody\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableFunExprBody\133useFir = true\135.txt"
index 67cf6c5..56ab8ec 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableFunExprBody\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableFunExprBody\133useFir = true\135.txt"
@@ -15,8 +15,7 @@
 
 @Composable
 fun Test(x: Int, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<A()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<A()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -24,6 +23,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithAndWithoutDefaultParams\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithAndWithoutDefaultParams\133useFir = false\135.txt"
index 3279caf..1e65559 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithAndWithoutDefaultParams\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithAndWithoutDefaultParams\133useFir = false\135.txt"
@@ -53,7 +53,7 @@
 @Composable
 fun Test(x: Int, y: Int, %composer: Composer?, %changed: Int, %default: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)<Wrap(1...>:Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,<Wrap(1...>:Test.kt")
   val %dirty = %changed
   if (%default and 0b0001 != 0) {
     %dirty = %dirty or 0b0110
@@ -76,7 +76,7 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(y)
-    Wrap(10, composableLambda(%composer, <>, true) { it: Int, %composer: Composer?, %changed: Int ->
+    Wrap(10, rememberComposableLambda(<>, true, { it: Int, %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<A(x)>:Test.kt")
       val %dirty = %changed
       if (%changed and 0b0110 == 0) {
@@ -94,7 +94,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b00110110)
+    }, %composer, 0b00110110), %composer, 0b00110110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithAndWithoutDefaultParams\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithAndWithoutDefaultParams\133useFir = true\135.txt"
index 0974d96..1961cb0 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithAndWithoutDefaultParams\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithAndWithoutDefaultParams\133useFir = true\135.txt"
@@ -53,7 +53,7 @@
 @Composable
 fun Test(x: Int, y: Int, %composer: Composer?, %changed: Int, %default: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)<Wrap(1...>:Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,<Wrap(1...>:Test.kt")
   val %dirty = %changed
   if (%default and 0b0001 != 0) {
     %dirty = %dirty or 0b0110
@@ -76,7 +76,7 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(y)
-    Wrap(10, composableLambda(%composer, <>, true) { it: @[ParameterName(name = 'x')] Int, %composer: Composer?, %changed: Int ->
+    Wrap(10, rememberComposableLambda(<>, true, { it: @[ParameterName(name = 'x')] Int, %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<A(x)>:Test.kt")
       val %dirty = %changed
       if (%changed and 0b0110 == 0) {
@@ -94,7 +94,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b00110110)
+    }, %composer, 0b00110110), %composer, 0b00110110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithReturnValue\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithReturnValue\133useFir = false\135.txt"
index cbef791..cddf917 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithReturnValue\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithReturnValue\133useFir = false\135.txt"
@@ -19,8 +19,7 @@
 
 @Composable
 fun Test(x: Int, y: Int, %composer: Composer?, %changed: Int, %default: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<A(x,>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<A(x,>:Test.kt")
   if (%default and 0b0001 != 0) {
     x = 0
   }
@@ -35,6 +34,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithReturnValue\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithReturnValue\133useFir = true\135.txt"
index cbef791..cddf917 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithReturnValue\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testComposableWithReturnValue\133useFir = true\135.txt"
@@ -19,8 +19,7 @@
 
 @Composable
 fun Test(x: Int, y: Int, %composer: Composer?, %changed: Int, %default: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<A(x,>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<A(x,>:Test.kt")
   if (%default and 0b0001 != 0) {
     x = 0
   }
@@ -35,6 +34,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testIfInLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testIfInLambda\133useFir = false\135.txt"
index 0228cbe..0d68784 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testIfInLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testIfInLambda\133useFir = false\135.txt"
@@ -26,7 +26,7 @@
 @Composable
 fun Test(x: Int, y: Int, %composer: Composer?, %changed: Int, %default: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)<Wrap>:Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,<Wrap>:Test.kt")
   val %dirty = %changed
   if (%default and 0b0001 != 0) {
     %dirty = %dirty or 0b0110
@@ -49,7 +49,7 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(y)
-    Wrap(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    Wrap(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C:Test.kt")
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -72,7 +72,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b0110)
+    }, %composer, 0b00110110), %composer, 0b0110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testIfInLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testIfInLambda\133useFir = true\135.txt"
index 0228cbe..0d68784 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testIfInLambda\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testIfInLambda\133useFir = true\135.txt"
@@ -26,7 +26,7 @@
 @Composable
 fun Test(x: Int, y: Int, %composer: Composer?, %changed: Int, %default: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)<Wrap>:Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,<Wrap>:Test.kt")
   val %dirty = %changed
   if (%default and 0b0001 != 0) {
     %dirty = %dirty or 0b0110
@@ -49,7 +49,7 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(y)
-    Wrap(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    Wrap(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C:Test.kt")
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -72,7 +72,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b0110)
+    }, %composer, 0b00110110), %composer, 0b0110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalComposableFunctions\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalComposableFunctions\133useFir = false\135.txt"
index e72a3c57..d676615e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalComposableFunctions\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalComposableFunctions\133useFir = false\135.txt"
@@ -33,8 +33,7 @@
     }
     @Composable
     fun Inner(%composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(Inner)<A(a)>:Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(Inner)<A(a)>:Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
@@ -42,7 +41,7 @@
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     Inner(%composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalComposableFunctions\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalComposableFunctions\133useFir = true\135.txt"
index e72a3c57..d676615e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalComposableFunctions\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalComposableFunctions\133useFir = true\135.txt"
@@ -33,8 +33,7 @@
     }
     @Composable
     fun Inner(%composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(Inner)<A(a)>:Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(Inner)<A(a)>:Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
@@ -42,7 +41,7 @@
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     Inner(%composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalFunction\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalFunction\133useFir = false\135.txt"
index 7ef5d06..297b6db 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalFunction\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalFunction\133useFir = false\135.txt"
@@ -33,8 +33,7 @@
     }
     @Composable
     fun foo(y: Int, %composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(foo)<B(x,>:Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(foo)<B(x,>:Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
@@ -42,7 +41,7 @@
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     foo(x, %composer, 0b1110 and %dirty)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalFunction\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalFunction\133useFir = true\135.txt"
index 7ef5d06..297b6db 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalFunction\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLocalFunction\133useFir = true\135.txt"
@@ -33,8 +33,7 @@
     }
     @Composable
     fun foo(y: Int, %composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(foo)<B(x,>:Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(foo)<B(x,>:Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
@@ -42,7 +41,7 @@
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     foo(x, %composer, 0b1110 and %dirty)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLoopWithContinueAndCallAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLoopWithContinueAndCallAfter\133useFir = false\135.txt"
index 628d59d..18102ab 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLoopWithContinueAndCallAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLoopWithContinueAndCallAfter\133useFir = false\135.txt"
@@ -26,12 +26,13 @@
 @Composable
 @NonRestartableComposable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<Call()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<Call()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   Call(%composer, 0)
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   val <iterator> = 0 .. 1.iterator()
   while (<iterator>.hasNext()) {
     val index = <iterator>.next()
@@ -45,8 +46,9 @@
     Call(%composer, 0)
     %composer.endReplaceableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLoopWithContinueAndCallAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLoopWithContinueAndCallAfter\133useFir = true\135.txt"
index 628d59d..18102ab 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLoopWithContinueAndCallAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testLoopWithContinueAndCallAfter\133useFir = true\135.txt"
@@ -26,12 +26,13 @@
 @Composable
 @NonRestartableComposable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<Call()>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<Call()>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   Call(%composer, 0)
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "")
   val <iterator> = 0 .. 1.iterator()
   while (<iterator>.hasNext()) {
     val index = <iterator>.next()
@@ -45,8 +46,9 @@
     Call(%composer, 0)
     %composer.endReplaceableGroup()
   }
+  %composer.endReplaceableGroup()
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testNestedCalls\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testNestedCalls\133useFir = false\135.txt"
index 7aa096c..cde46a5 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testNestedCalls\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testNestedCalls\133useFir = false\135.txt"
@@ -25,7 +25,7 @@
 @Composable
 fun A(x: Int, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(A)<Provid...>,<B(x)>:Test.kt")
+  sourceInformation(%composer, "C(A)<{>,<Provid...>,<B(x)>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b0110 == 0) {
     %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
@@ -34,8 +34,8 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    Provide(composableLambda(%composer, <>, true) { y: Int, %composer: Composer?, %changed: Int ->
-      sourceInformation(%composer, "C<Provid...>,<B(x,>:Test.kt")
+    Provide(rememberComposableLambda(<>, true, { y: Int, %composer: Composer?, %changed: Int ->
+      sourceInformation(%composer, "C<{>,<Provid...>,<B(x,>:Test.kt")
       val %dirty = %changed
       if (%changed and 0b0110 == 0) {
         %dirty = %dirty or if (%composer.changed(y)) 0b0100 else 0b0010
@@ -44,7 +44,7 @@
         if (isTraceInProgress()) {
           traceEventStart(<>, %dirty, -1, <>)
         }
-        Provide(composableLambda(%composer, <>, true) { z: Int, %composer: Composer?, %changed: Int ->
+        Provide(rememberComposableLambda(<>, true, { z: Int, %composer: Composer?, %changed: Int ->
           sourceInformation(%composer, "C<B(x,>:Test.kt")
           val %dirty = %changed
           if (%changed and 0b0110 == 0) {
@@ -61,7 +61,7 @@
           } else {
             %composer.skipToGroupEnd()
           }
-        }, %composer, 0b0110)
+        }, %composer, 0b00110110), %composer, 0b0110)
         B(x, y, 0, %composer, 0b01110000 and %dirty shl 0b0011, 0b0100)
         if (isTraceInProgress()) {
           traceEventEnd()
@@ -69,7 +69,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b0110)
+    }, %composer, 0b00110110), %composer, 0b0110)
     B(x, 0, 0, %composer, 0b1110 and %dirty, 0b0110)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testNestedCalls\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testNestedCalls\133useFir = true\135.txt"
index 7aa096c..cde46a5 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testNestedCalls\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/testNestedCalls\133useFir = true\135.txt"
@@ -25,7 +25,7 @@
 @Composable
 fun A(x: Int, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(A)<Provid...>,<B(x)>:Test.kt")
+  sourceInformation(%composer, "C(A)<{>,<Provid...>,<B(x)>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b0110 == 0) {
     %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
@@ -34,8 +34,8 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    Provide(composableLambda(%composer, <>, true) { y: Int, %composer: Composer?, %changed: Int ->
-      sourceInformation(%composer, "C<Provid...>,<B(x,>:Test.kt")
+    Provide(rememberComposableLambda(<>, true, { y: Int, %composer: Composer?, %changed: Int ->
+      sourceInformation(%composer, "C<{>,<Provid...>,<B(x,>:Test.kt")
       val %dirty = %changed
       if (%changed and 0b0110 == 0) {
         %dirty = %dirty or if (%composer.changed(y)) 0b0100 else 0b0010
@@ -44,7 +44,7 @@
         if (isTraceInProgress()) {
           traceEventStart(<>, %dirty, -1, <>)
         }
-        Provide(composableLambda(%composer, <>, true) { z: Int, %composer: Composer?, %changed: Int ->
+        Provide(rememberComposableLambda(<>, true, { z: Int, %composer: Composer?, %changed: Int ->
           sourceInformation(%composer, "C<B(x,>:Test.kt")
           val %dirty = %changed
           if (%changed and 0b0110 == 0) {
@@ -61,7 +61,7 @@
           } else {
             %composer.skipToGroupEnd()
           }
-        }, %composer, 0b0110)
+        }, %composer, 0b00110110), %composer, 0b0110)
         B(x, y, 0, %composer, 0b01110000 and %dirty shl 0b0011, 0b0100)
         if (isTraceInProgress()) {
           traceEventEnd()
@@ -69,7 +69,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b0110)
+    }, %composer, 0b00110110), %composer, 0b0110)
     B(x, 0, 0, %composer, 0b1110 and %dirty, 0b0110)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/test_InlineForLoop\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/test_InlineForLoop\133useFir = false\135.txt"
index 1249c09..8eedb86 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/test_InlineForLoop\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/test_InlineForLoop\133useFir = false\135.txt"
@@ -46,12 +46,14 @@
 @Composable
 @ComposableInferredTarget(scheme = "[0[0]]")
 fun <T> Bug(items: List<T>, content: Function3<@[ParameterName(name = 'item')] T, Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
+  sourceInformationMarkerStart(%composer, <>, "CC(Bug)P(1):Test.kt")
   %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(Bug)P(1)*<conten...>:Test.kt")
+  sourceInformation(%composer, "*<conten...>")
   val <iterator> = items.iterator()
   while (<iterator>.hasNext()) {
     val item = <iterator>.next()
     content(item, %composer, 0b01110000 and %changed)
   }
   %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/test_InlineForLoop\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/test_InlineForLoop\133useFir = true\135.txt"
index 212f2a8..587adfa 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/test_InlineForLoop\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTests/test_InlineForLoop\133useFir = true\135.txt"
@@ -46,12 +46,14 @@
 @Composable
 @ComposableInferredTarget(scheme = "[0[0]]")
 fun <T> Bug(items: List<T>, content: Function3<@[ParameterName(name = 'item')] T, Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
+  sourceInformationMarkerStart(%composer, <>, "CC(Bug)P(1):Test.kt")
   %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(Bug)P(1)*<conten...>:Test.kt")
+  sourceInformation(%composer, "*<conten...>")
   val <iterator> = items.iterator()
   while (<iterator>.hasNext()) {
     val item = <iterator>.next()
     content(item, %composer, 0b01110000 and %changed)
   }
   %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = false\135.txt"
index b7b5ab6..4c95cac 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = false\135.txt"
@@ -37,14 +37,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Example(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         Consumer { it: Int ->
           println(int)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
@@ -59,7 +58,6 @@
 }
 @Composable
 fun Example(consumer: Consumer, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(Example):Test.kt")
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerStart(%composer, <>, "CC(Example):Test.kt")
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = true\135.txt"
index b7b5ab6..4c95cac 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = true\135.txt"
@@ -37,14 +37,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Example(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         Consumer { it: Int ->
           println(int)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
@@ -59,7 +58,6 @@
 }
 @Composable
 fun Example(consumer: Consumer, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(Example):Test.kt")
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerStart(%composer, <>, "CC(Example):Test.kt")
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testFunInterfaces\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testFunInterfaces\133useFir = false\135.txt"
index 5b73ea0..9d23402 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testFunInterfaces\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testFunInterfaces\133useFir = false\135.txt"
@@ -38,14 +38,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Example(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 || %dirty and 0b1000 != 0 && %composer.changedInstance(a)) {
         A { it: Int ->
           a.compute(it)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testFunInterfaces\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testFunInterfaces\133useFir = true\135.txt"
index 5b73ea0..9d23402 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testFunInterfaces\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testFunInterfaces\133useFir = true\135.txt"
@@ -38,14 +38,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Example(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 || %dirty and 0b1000 != 0 && %composer.changedInstance(a)) {
         A { it: Int ->
           a.compute(it)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testNoCaptureFunInterface\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testNoCaptureFunInterface\133useFir = false\135.txt"
index 4e5db6e..8c0c69e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testNoCaptureFunInterface\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testNoCaptureFunInterface\133useFir = false\135.txt"
@@ -47,7 +47,6 @@
 }
 @Composable
 fun Example(consumer: Consumer, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(Example):Test.kt")
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerStart(%composer, <>, "CC(Example):Test.kt")
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testNoCaptureFunInterface\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testNoCaptureFunInterface\133useFir = true\135.txt"
index 4e5db6e..8c0c69e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testNoCaptureFunInterface\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testNoCaptureFunInterface\133useFir = true\135.txt"
@@ -47,7 +47,6 @@
 }
 @Composable
 fun Example(consumer: Consumer, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(Example):Test.kt")
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerStart(%composer, <>, "CC(Example):Test.kt")
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = false\135.txt"
index e52b302..94c62c5 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = false\135.txt"
@@ -14,25 +14,23 @@
 
 @Composable
 fun Test(foo: Foo, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<{>,<Consum...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<{>,<Consum...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = Consume(<block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(foo) || %changed and 0b0110 == 0b0100) {
       {
         foo.value
       }
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }, %composer, 0)
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = true\135.txt"
index e52b302..94c62c5 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = true\135.txt"
@@ -14,25 +14,23 @@
 
 @Composable
 fun Test(foo: Foo, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<{>,<Consum...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<{>,<Consum...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = Consume(<block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(foo) || %changed and 0b0110 == 0b0100) {
       {
         foo.value
       }
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }, %composer, 0)
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testAdaptedFunctionRef\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testAdaptedFunctionRef\133useFir = false\135.txt"
index 5785249..c7aa485 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testAdaptedFunctionRef\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testAdaptedFunctionRef\133useFir = false\135.txt"
@@ -40,8 +40,7 @@
 }
 @Composable
 fun rememberFooInline(%composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(rememberFooInline)<fooInl...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(rememberFooInline)<fooInl...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -57,20 +56,18 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun rememberFoo(%composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(rememberFoo)<scroll...>,<foo(sc...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(rememberFoo)<scroll...>,<foo(sc...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = foo(<block>{
     val tmp0 = scrollState()
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%composer.changed(tmp0)) {
       fun ScrollState.test(p0: Int): Int {
         receiver.test(
@@ -79,19 +76,18 @@
       }
       tmp0::test
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }, %composer, 0)
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun rememberFooExactInline(%composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(rememberFooExactInline)<fooInl...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(rememberFooExactInline)<fooInl...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -99,44 +95,40 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun rememberFooExact(%composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(rememberFooExact)<scroll...>,<foo(sc...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(rememberFooExact)<scroll...>,<foo(sc...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = foo(<block>{
     val tmp0 = scrollState()
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%composer.changed(tmp0)) {
       tmp0::testExact
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }, %composer, 0)
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun fooInline(block: Function1<Int, Int>, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(fooInline):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "CC(fooInline):Test.kt")
   val tmp0 = block(0)
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun foo(block: Function1<Int, Int>, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(foo):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(foo):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -144,6 +136,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testAdaptedFunctionRef\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testAdaptedFunctionRef\133useFir = true\135.txt"
index ab1b9ff..1bb6137 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testAdaptedFunctionRef\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testAdaptedFunctionRef\133useFir = true\135.txt"
@@ -40,8 +40,7 @@
 }
 @Composable
 fun rememberFooInline(%composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(rememberFooInline)<fooInl...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(rememberFooInline)<fooInl...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -57,20 +56,18 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun rememberFoo(%composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(rememberFoo)<test>,<foo(sc...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(rememberFoo)<test>,<foo(sc...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = foo(<block>{
     val tmp0 = scrollState()
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%composer.changed(tmp0)) {
       fun ScrollState.test(p0: Int): Int {
         receiver.test(
@@ -79,19 +76,18 @@
       }
       tmp0::test
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }, %composer, 0)
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun rememberFooExactInline(%composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(rememberFooExactInline)<fooInl...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(rememberFooExactInline)<fooInl...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -99,44 +95,40 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun rememberFooExact(%composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(rememberFooExact)<testEx...>,<foo(sc...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(rememberFooExact)<testEx...>,<foo(sc...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = foo(<block>{
     val tmp0 = scrollState()
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%composer.changed(tmp0)) {
       tmp0::testExact
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }, %composer, 0)
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun fooInline(block: Function1<Int, Int>, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(fooInline):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "CC(fooInline):Test.kt")
   val tmp0 = block(0)
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
 @Composable
 fun foo(block: Function1<Int, Int>, %composer: Composer?, %changed: Int): Int {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(foo):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(foo):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -144,6 +136,6 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposabableLambdaInLocalDeclaration\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposabableLambdaInLocalDeclaration\133useFir = false\135.txt"
index 432c028..2db907c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposabableLambdaInLocalDeclaration\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposabableLambdaInLocalDeclaration\133useFir = false\135.txt"
@@ -19,7 +19,7 @@
 @Composable
 fun Test(enabled: Boolean, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)<Wrap(c...>:Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,<Wrap(c...>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(enabled)) 0b0100 else 0b0010
@@ -28,7 +28,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val content = composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    val content = rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<Displa...>:Test.kt")
       if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -41,7 +41,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }
+    }, %composer, 0b00110110)
     Wrap(content, %composer, 0b0110)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposabableLambdaInLocalDeclaration\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposabableLambdaInLocalDeclaration\133useFir = true\135.txt"
index 432c028..2db907c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposabableLambdaInLocalDeclaration\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposabableLambdaInLocalDeclaration\133useFir = true\135.txt"
@@ -19,7 +19,7 @@
 @Composable
 fun Test(enabled: Boolean, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)<Wrap(c...>:Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,<Wrap(c...>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(enabled)) 0b0100 else 0b0010
@@ -28,7 +28,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val content = composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    val content = rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<Displa...>:Test.kt")
       if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -41,7 +41,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }
+    }, %composer, 0b00110110)
     Wrap(content, %composer, 0b0110)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposableInAFunctionParameter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposableInAFunctionParameter\133useFir = false\135.txt"
index c60bcb3..2169259 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposableInAFunctionParameter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposableInAFunctionParameter\133useFir = false\135.txt"
@@ -19,7 +19,7 @@
 @Composable
 fun Test(enabled: Boolean, content: Function2<Composer, Int, Unit>?, %composer: Composer?, %changed: Int, %default: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)P(1)<Wrap(c...>:Test.kt")
+  sourceInformation(%composer, "C(Test)P(1)<{>,<Wrap(c...>:Test.kt")
   val %dirty = %changed
   if (%default and 0b0001 != 0) {
     %dirty = %dirty or 0b0110
@@ -33,7 +33,7 @@
   }
   if (%dirty and 0b01011011 != 0b00010010 || !%composer.skipping) {
     if (%default and 0b0010 != 0) {
-      content = composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+      content = rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
         sourceInformation(%composer, "C<Displa...>:Test.kt")
         if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
           if (isTraceInProgress()) {
@@ -46,7 +46,7 @@
         } else {
           %composer.skipToGroupEnd()
         }
-      }
+      }, %composer, 0b00110110)
     }
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposableInAFunctionParameter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposableInAFunctionParameter\133useFir = true\135.txt"
index c60bcb3..2169259 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposableInAFunctionParameter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testComposableInAFunctionParameter\133useFir = true\135.txt"
@@ -19,7 +19,7 @@
 @Composable
 fun Test(enabled: Boolean, content: Function2<Composer, Int, Unit>?, %composer: Composer?, %changed: Int, %default: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)P(1)<Wrap(c...>:Test.kt")
+  sourceInformation(%composer, "C(Test)P(1)<{>,<Wrap(c...>:Test.kt")
   val %dirty = %changed
   if (%default and 0b0001 != 0) {
     %dirty = %dirty or 0b0110
@@ -33,7 +33,7 @@
   }
   if (%dirty and 0b01011011 != 0b00010010 || !%composer.skipping) {
     if (%default and 0b0010 != 0) {
-      content = composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+      content = rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
         sourceInformation(%composer, "C<Displa...>:Test.kt")
         if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
           if (isTraceInProgress()) {
@@ -46,7 +46,7 @@
         } else {
           %composer.skipToGroupEnd()
         }
-      }
+      }, %composer, 0b00110110)
     }
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = false\135.txt"
index 9f86c1e..d209b05 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = false\135.txt"
@@ -28,14 +28,13 @@
       use(x)
     }
     val shouldMemoize = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           ::foo
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = true\135.txt"
index 9f86c1e..d209b05 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = true\135.txt"
@@ -28,14 +28,13 @@
       use(x)
     }
     val shouldMemoize = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           ::foo
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = false\135.txt"
index ecf2150..d712dbf 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = false\135.txt"
@@ -56,14 +56,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     TestLambda(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           println("Captures a" + a)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = true\135.txt"
index ecf2150..d712dbf 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = true\135.txt"
@@ -56,14 +56,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     TestLambda(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           println("Captures a" + a)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = false\135.txt"
index 87d306b..e80e4db 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = false\135.txt"
@@ -24,8 +24,7 @@
 @NonRestartableComposable
 @Composable
 fun Err(y: Int, z: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Err)<{>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Err)<{>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -35,17 +34,16 @@
       return x + y + w
     }
   }
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(y) || %changed and 0b0110 == 0b0100 or %changed and 0b01110000 xor 0b00110000 > 32 && %composer.changed(z) || %changed and 0b00110000 == 0b00100000) {
     {
       Local().something(2)
     }
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = true\135.txt"
index 87d306b..e80e4db 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = true\135.txt"
@@ -24,8 +24,7 @@
 @NonRestartableComposable
 @Composable
 fun Err(y: Int, z: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Err)<{>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Err)<{>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -35,17 +34,16 @@
       return x + y + w
     }
   }
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(y) || %changed and 0b0110 == 0b0100 or %changed and 0b01110000 xor 0b00110000 > 32 && %composer.changed(z) || %changed and 0b00110000 == 0b00100000) {
     {
       Local().something(2)
     }
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = false\135.txt"
index 18fab7a..c0c2d49 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = false\135.txt"
@@ -21,8 +21,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(z: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<{>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<{>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -30,18 +29,17 @@
     val y: Int = z
   }
   val lambda = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(z) || %changed and 0b0110 == 0b0100) {
       {
         Foo(1)
       }
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = true\135.txt"
index 18fab7a..c0c2d49 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = true\135.txt"
@@ -21,8 +21,7 @@
 @NonRestartableComposable
 @Composable
 fun Example(z: Int, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<{>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<{>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -30,18 +29,17 @@
     val y: Int = z
   }
   val lambda = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(z) || %changed and 0b0110 == 0b0100) {
       {
         Foo(1)
       }
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures3\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures3\133useFir = false\135.txt"
index 653cafe..46cdb0e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures3\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures3\133useFir = false\135.txt"
@@ -20,24 +20,23 @@
 @Composable
 fun SimpleAnimatedContentSample(%composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(SimpleAnimatedContentSample)<Animat...>:Test.kt")
+  sourceInformation(%composer, "C(SimpleAnimatedContentSample)<{>,<Animat...>:Test.kt")
   if (%changed != 0 || !%composer.skipping) {
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
     @Composable
     fun Foo(%composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(Foo):Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(Foo):Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
-    AnimatedContent(1.0f, composableLambda(%composer, <>, false) { it: Float, %composer: Composer?, %changed: Int ->
+    AnimatedContent(1.0f, rememberComposableLambda(<>, false, { it: Float, %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<Foo()>:Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
@@ -46,7 +45,7 @@
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-    }, %composer, 0b00110110)
+    }, %composer, 0b00110110), %composer, 0b00110110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures3\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures3\133useFir = true\135.txt"
index aadb90e..ea82b0b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures3\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures3\133useFir = true\135.txt"
@@ -20,24 +20,23 @@
 @Composable
 fun SimpleAnimatedContentSample(%composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(SimpleAnimatedContentSample)<Animat...>:Test.kt")
+  sourceInformation(%composer, "C(SimpleAnimatedContentSample)<{>,<Animat...>:Test.kt")
   if (%changed != 0 || !%composer.skipping) {
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
     @Composable
     fun Foo(%composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(Foo):Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(Foo):Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
-    AnimatedContent(1.0f, composableLambda(%composer, <>, false) { it: @[ParameterName(name = 'targetState')] Float, %composer: Composer?, %changed: Int ->
+    AnimatedContent(1.0f, rememberComposableLambda(<>, false, { it: @[ParameterName(name = 'targetState')] Float, %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<Foo()>:Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
@@ -46,7 +45,7 @@
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-    }, %composer, 0b00110110)
+    }, %composer, 0b00110110), %composer, 0b00110110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures\133useFir = false\135.txt"
index 6f6a11d..075c1d3 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures\133useFir = false\135.txt"
@@ -25,8 +25,7 @@
 @NonRestartableComposable
 @Composable
 fun Err(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Err):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Err):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -41,5 +40,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures\133useFir = true\135.txt"
index 6f6a11d..075c1d3 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunCaptures\133useFir = true\135.txt"
@@ -25,8 +25,7 @@
 @NonRestartableComposable
 @Composable
 fun Err(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Err):Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Err):Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -41,5 +40,5 @@
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = false\135.txt"
index 8739b0c..35047f53 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = false\135.txt"
@@ -36,12 +36,11 @@
     }
     val x = <block>{
       val tmp0 = rcvr
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 == 0b00100000) {
         tmp0::method
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = true\135.txt"
index 6e1af1c..9e51797 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = true\135.txt"
@@ -36,12 +36,11 @@
     }
     val x = <block>{
       val tmp0 = rcvr
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 == 0b00100000) {
         tmp0::method
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = false\135.txt"
index 444d05d..004f73f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = false\135.txt"
@@ -32,12 +32,11 @@
       println(param)
     }
     val x = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         ::method
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = true\135.txt"
index 9b24a99..aa2e321 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = true\135.txt"
@@ -32,12 +32,11 @@
       println(param)
     }
     val x = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         ::method
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalInALocal\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalInALocal\133useFir = false\135.txt"
index 36d03b7..a842339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalInALocal\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalInALocal\133useFir = false\135.txt"
@@ -26,21 +26,19 @@
     }
     @Composable
     fun A(%composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(A):Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(A):Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     @Composable
     @ComposableInferredTarget(scheme = "[0[0]]")
     fun B(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(B)<conten...>:Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(B)<conten...>:Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
@@ -48,16 +46,15 @@
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     @Composable
     fun C(%composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(C)<B>:Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(C)<{>,<B>:Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
-      B(composableLambda(%composer, <>, false) { %composer: Composer?, %changed: Int ->
+      B(rememberComposableLambda(<>, false, { %composer: Composer?, %changed: Int ->
         sourceInformation(%composer, "C<A()>:Test.kt")
         if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
           if (isTraceInProgress()) {
@@ -70,11 +67,11 @@
         } else {
           %composer.skipToGroupEnd()
         }
-      }, %composer, 0b0110)
+      }, %composer, 0b00110110), %composer, 0b0110)
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalInALocal\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalInALocal\133useFir = true\135.txt"
index 36d03b7..a842339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalInALocal\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalInALocal\133useFir = true\135.txt"
@@ -26,21 +26,19 @@
     }
     @Composable
     fun A(%composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(A):Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(A):Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     @Composable
     @ComposableInferredTarget(scheme = "[0[0]]")
     fun B(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(B)<conten...>:Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(B)<conten...>:Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
@@ -48,16 +46,15 @@
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     @Composable
     fun C(%composer: Composer?, %changed: Int) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "C(C)<B>:Test.kt")
+      sourceInformationMarkerStart(%composer, <>, "C(C)<{>,<B>:Test.kt")
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
-      B(composableLambda(%composer, <>, false) { %composer: Composer?, %changed: Int ->
+      B(rememberComposableLambda(<>, false, { %composer: Composer?, %changed: Int ->
         sourceInformation(%composer, "C<A()>:Test.kt")
         if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
           if (isTraceInProgress()) {
@@ -70,11 +67,11 @@
         } else {
           %composer.skipToGroupEnd()
         }
-      }, %composer, 0b0110)
+      }, %composer, 0b00110110), %composer, 0b0110)
       if (isTraceInProgress()) {
         traceEventEnd()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = false\135.txt"
index 6f62963..1f82a6f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = false\135.txt"
@@ -35,14 +35,13 @@
       %composer.startReplaceableGroup(<>)
       sourceInformation(%composer, "<{>")
       val tmp1_group = if (param != null) {
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
           {
             param()
           }
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       } else {
         null
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = true\135.txt"
index 6f62963..1f82a6f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = true\135.txt"
@@ -35,14 +35,13 @@
       %composer.startReplaceableGroup(<>)
       sourceInformation(%composer, "<{>")
       val tmp1_group = if (param != null) {
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
           {
             param()
           }
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       } else {
         null
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = false\135.txt"
index 6ac271b..bea2f76 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = false\135.txt"
@@ -31,22 +31,20 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val x = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Stable()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val shouldMemoize = <block>{
       val tmp0 = x
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(false) {
         tmp0::qux
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = true\135.txt"
index a1bbcd7..84aa062 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = true\135.txt"
@@ -31,22 +31,20 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val x = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Stable()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val shouldMemoize = <block>{
       val tmp0 = x
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(false) {
         tmp0::qux
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = false\135.txt"
index 1f91ed8..5467e44 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = false\135.txt"
@@ -31,22 +31,20 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val x = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Stable()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val shouldMemoize = <block>{
       val tmp0 = x
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(false) {
         tmp0::qux
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = true\135.txt"
index a169c19..49d806b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = true\135.txt"
@@ -31,22 +31,20 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val x = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Stable()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val shouldMemoize = <block>{
       val tmp0 = x
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(false) {
         tmp0::qux
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = false\135.txt"
index 7511d6d..c43c122 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = false\135.txt"
@@ -39,12 +39,11 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val x = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Stable()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val shouldNotMemoize = x::qux
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = true\135.txt"
index 7511d6d..c43c122 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = true\135.txt"
@@ -39,12 +39,11 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val x = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Stable()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val shouldNotMemoize = x::qux
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = false\135.txt"
index 3b246ec..25277df 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = false\135.txt"
@@ -20,32 +20,29 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<rememb...>,<x::foo>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<rememb...>,<x::foo>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val x = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(false) {
       Stable()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
   }
   val shouldMemoize = <block>{
     val tmp0 = x
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(false) {
       tmp0::foo
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = true\135.txt"
index d1ea51b..abcd7fa 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = true\135.txt"
@@ -20,32 +20,29 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<rememb...>,<foo>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<rememb...>,<foo>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val x = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(false) {
       Stable()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
   }
   val shouldMemoize = <block>{
     val tmp0 = x
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(false) {
       tmp0::foo
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = false\135.txt"
index 232587d..02a8370 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = false\135.txt"
@@ -20,23 +20,21 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val x = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(false) {
       Unstable()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
   }
   val shouldNotMemoize = x::foo
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = true\135.txt"
index 232587d..02a8370 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = true\135.txt"
@@ -20,23 +20,21 @@
 @NonRestartableComposable
 @Composable
 fun Example(%composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Example)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val x = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(false) {
       Unstable()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
   }
   val shouldNotMemoize = x::foo
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = false\135.txt"
index f8d75e2..a162a49 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = false\135.txt"
@@ -26,8 +26,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(false) {
       composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
         sourceInformation(%composer, "C<Text(s...>:Test.kt")
@@ -44,7 +43,7 @@
         }
       }
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group(%composer, 6)
     %composer.cache(false) {
       composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = true\135.txt"
index f8d75e2..a162a49 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = true\135.txt"
@@ -26,8 +26,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(false) {
       composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
         sourceInformation(%composer, "C<Text(s...>:Test.kt")
@@ -44,7 +43,7 @@
         }
       }
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group(%composer, 6)
     %composer.cache(false) {
       composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testStateDelegateCapture\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testStateDelegateCapture\133useFir = false\135.txt"
index 60c8034..4a53363 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testStateDelegateCapture\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testStateDelegateCapture\133useFir = false\135.txt"
@@ -20,7 +20,7 @@
 @Composable
 fun A(%composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(A)<B>:Test.kt")
+  sourceInformation(%composer, "C(A)<{>,<B>:Test.kt")
   if (%changed != 0 || !%composer.skipping) {
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
@@ -33,7 +33,7 @@
         return x%delegate.getValue(null, ::x%delegate)
       }
     }
-    B(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    B(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C:Test.kt")
       if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -46,7 +46,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b0110)
+    }, %composer, 0b00110110), %composer, 0b0110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testStateDelegateCapture\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testStateDelegateCapture\133useFir = true\135.txt"
index 60c8034..4a53363 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testStateDelegateCapture\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testStateDelegateCapture\133useFir = true\135.txt"
@@ -20,7 +20,7 @@
 @Composable
 fun A(%composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(A)<B>:Test.kt")
+  sourceInformation(%composer, "C(A)<{>,<B>:Test.kt")
   if (%changed != 0 || !%composer.skipping) {
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
@@ -33,7 +33,7 @@
         return x%delegate.getValue(null, ::x%delegate)
       }
     }
-    B(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    B(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C:Test.kt")
       if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -46,7 +46,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b0110)
+    }, %composer, 0b00110110), %composer, 0b0110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallBeforeRemember\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallBeforeRemember\133useFir = false\135.txt"
index b64e150..13490ed 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallBeforeRemember\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallBeforeRemember\133useFir = false\135.txt"
@@ -26,12 +26,11 @@
     }
     A(%composer, 0)
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallBeforeRemember\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallBeforeRemember\133useFir = true\135.txt"
index b64e150..13490ed 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallBeforeRemember\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallBeforeRemember\133useFir = true\135.txt"
@@ -26,12 +26,11 @@
     }
     A(%composer, 0)
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallInArgument\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallInArgument\133useFir = false\135.txt"
index 79e0e87..613c7b9d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallInArgument\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallInArgument\133useFir = false\135.txt"
@@ -25,12 +25,11 @@
     }
     val foo = <block>{
       val tmp0_remember%arg%0 = CInt(%composer, 0)
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(%composer.changed(tmp0_remember%arg%0)) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallInArgument\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallInArgument\133useFir = true\135.txt"
index 79e0e87..613c7b9d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallInArgument\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testComposableCallInArgument\133useFir = true\135.txt"
@@ -25,12 +25,11 @@
     }
     val foo = <block>{
       val tmp0_remember%arg%0 = CInt(%composer, 0)
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(%composer.changed(tmp0_remember%arg%0)) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallAsInput\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallAsInput\133useFir = false\135.txt"
index 73f6fab..cf0f54c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallAsInput\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallAsInput\133useFir = false\135.txt"
@@ -25,12 +25,11 @@
     }
     val foo = <block>{
       val tmp0_remember%arg%0 = compositionLocalBar.<get-current>(%composer, 0b0110)
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(%composer.changed(tmp0_remember%arg%0)) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallAsInput\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallAsInput\133useFir = true\135.txt"
index 73f6fab..cf0f54c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallAsInput\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallAsInput\133useFir = true\135.txt"
@@ -25,12 +25,11 @@
     }
     val foo = <block>{
       val tmp0_remember%arg%0 = compositionLocalBar.<get-current>(%composer, 0b0110)
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(%composer.changed(tmp0_remember%arg%0)) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallBeforeRemember\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallBeforeRemember\133useFir = false\135.txt"
index dd05768..658bb10 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallBeforeRemember\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallBeforeRemember\133useFir = false\135.txt"
@@ -26,12 +26,11 @@
     }
     val bar = compositionLocalBar.<get-current>(%composer, 0b0110)
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%composer.changed(bar)) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallBeforeRemember\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallBeforeRemember\133useFir = true\135.txt"
index dd05768..658bb10 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallBeforeRemember\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testCompositionLocalCallBeforeRemember\133useFir = true\135.txt"
@@ -26,12 +26,11 @@
     }
     val bar = compositionLocalBar.<get-current>(%composer, 0b0110)
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%composer.changed(bar)) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testElidedRememberInsideIfDeoptsRememberAfterIf\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testElidedRememberInsideIfDeoptsRememberAfterIf\133useFir = false\135.txt"
index 675b37e..5127fc0 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testElidedRememberInsideIfDeoptsRememberAfterIf\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testElidedRememberInsideIfDeoptsRememberAfterIf\133useFir = false\135.txt"
@@ -22,8 +22,7 @@
 @Composable
 @NonRestartableComposable
 fun app(x: Boolean, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(app)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(app)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -31,12 +30,11 @@
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "<rememb...>")
     val tmp1_group = if (x) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         1
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     } else {
       2
@@ -45,16 +43,15 @@
     tmp1_group
   }
   val b = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp2_group = %composer.cache(false) {
       2
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp2_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testElidedRememberInsideIfDeoptsRememberAfterIf\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testElidedRememberInsideIfDeoptsRememberAfterIf\133useFir = true\135.txt"
index 675b37e..5127fc0 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testElidedRememberInsideIfDeoptsRememberAfterIf\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testElidedRememberInsideIfDeoptsRememberAfterIf\133useFir = true\135.txt"
@@ -22,8 +22,7 @@
 @Composable
 @NonRestartableComposable
 fun app(x: Boolean, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(app)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(app)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -31,12 +30,11 @@
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "<rememb...>")
     val tmp1_group = if (x) {
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         1
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     } else {
       2
@@ -45,16 +43,15 @@
     tmp1_group
   }
   val b = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp2_group = %composer.cache(false) {
       2
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp2_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testForEarlyExit\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testForEarlyExit\133useFir = false\135.txt"
index e7b6f84..b64bd92 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testForEarlyExit\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testForEarlyExit\133useFir = false\135.txt"
@@ -29,14 +29,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val value = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         mutableStateOf(
           value = false
         )
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (!value.value && !condition) {
@@ -49,14 +48,13 @@
       return
     }
     val value2 = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(false) {
         mutableStateOf(
           value = false
         )
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     Text("Text %{value.value}, %{value2.value}", %composer, 0)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testForEarlyExit\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testForEarlyExit\133useFir = true\135.txt"
index e7b6f84..b64bd92 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testForEarlyExit\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testForEarlyExit\133useFir = true\135.txt"
@@ -29,14 +29,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val value = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         mutableStateOf(
           value = false
         )
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (!value.value && !condition) {
@@ -49,14 +48,13 @@
       return
     }
     val value2 = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(false) {
         mutableStateOf(
           value = false
         )
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     Text("Text %{value.value}, %{value2.value}", %composer, 0)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = false\135.txt"
index 98b7759..651b3ff 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = false\135.txt"
@@ -40,12 +40,11 @@
     if (%changed and 0b0001 == 0 || %composer.defaultsInvalid) {
       if (%default and 0b0001 != 0) {
         a = <block>{
-          %composer.startReplaceableGroup(<>)
-          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+          sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
           val tmp0_group = %composer.cache(false) {
             0
           }
-          %composer.endReplaceableGroup()
+          sourceInformationMarkerEnd(%composer)
           tmp0_group
         }
       }
@@ -55,12 +54,11 @@
       }
       if (%default and 0b0100 != 0) {
         c = <block>{
-          %composer.startReplaceableGroup(<>)
-          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+          sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
           val tmp1_group = %composer.cache(false) {
             0
           }
-          %composer.endReplaceableGroup()
+          sourceInformationMarkerEnd(%composer)
           tmp1_group
         }
       }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = true\135.txt"
index 98b7759..651b3ff 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = true\135.txt"
@@ -40,12 +40,11 @@
     if (%changed and 0b0001 == 0 || %composer.defaultsInvalid) {
       if (%default and 0b0001 != 0) {
         a = <block>{
-          %composer.startReplaceableGroup(<>)
-          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+          sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
           val tmp0_group = %composer.cache(false) {
             0
           }
-          %composer.endReplaceableGroup()
+          sourceInformationMarkerEnd(%composer)
           tmp0_group
         }
       }
@@ -55,12 +54,11 @@
       }
       if (%default and 0b0100 != 0) {
         c = <block>{
-          %composer.startReplaceableGroup(<>)
-          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+          sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
           val tmp1_group = %composer.cache(false) {
             0
           }
-          %composer.endReplaceableGroup()
+          sourceInformationMarkerEnd(%composer)
           tmp1_group
         }
       }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = false\135.txt"
index b7e60f1..2cbb30a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = false\135.txt"
@@ -28,12 +28,11 @@
   if (%dirty and 0b1011 != 0b0010 || !%composer.skipping) {
     if (%default and 0b0001 != 0) {
       a = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           0
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = true\135.txt"
index b7e60f1..2cbb30a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = true\135.txt"
@@ -28,12 +28,11 @@
   if (%dirty and 0b1011 != 0b0010 || !%composer.skipping) {
     if (%default and 0b0001 != 0) {
       a = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           0
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfLambdaInIfBlock\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfLambdaInIfBlock\133useFir = false\135.txt"
index 44c4376..ae188a3 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfLambdaInIfBlock\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfLambdaInIfBlock\133useFir = false\135.txt"
@@ -50,8 +50,7 @@
       val dismissModifier = <block>{
         val tmp1_group = if (visible) {
           m.pointerInput(Unit, <block>{
-            %composer.startReplaceableGroup(<>)
-            sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+            sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
             val tmp0_group = %composer.cache(%dirty and 0b001110000000 == 0b000100000000) {
               {
                 detectTapGestures {
@@ -59,7 +58,7 @@
                 }
               }
             }
-            %composer.endReplaceableGroup()
+            sourceInformationMarkerEnd(%composer)
             tmp0_group
           })
         } else {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfLambdaInIfBlock\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfLambdaInIfBlock\133useFir = true\135.txt"
index 44c4376..ae188a3 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfLambdaInIfBlock\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfLambdaInIfBlock\133useFir = true\135.txt"
@@ -50,8 +50,7 @@
       val dismissModifier = <block>{
         val tmp1_group = if (visible) {
           m.pointerInput(Unit, <block>{
-            %composer.startReplaceableGroup(<>)
-            sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+            sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
             val tmp0_group = %composer.cache(%dirty and 0b001110000000 == 0b000100000000) {
               {
                 detectTapGestures {
@@ -59,7 +58,7 @@
                 }
               }
             }
-            %composer.endReplaceableGroup()
+            sourceInformationMarkerEnd(%composer)
             tmp0_group
           })
         } else {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMemoizationWStableCapture\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMemoizationWStableCapture\133useFir = false\135.txt"
index f4fb2cc..54f77c7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMemoizationWStableCapture\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMemoizationWStableCapture\133useFir = false\135.txt"
@@ -27,14 +27,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Wrapper(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           println(param)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMemoizationWStableCapture\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMemoizationWStableCapture\133useFir = true\135.txt"
index f4fb2cc..54f77c7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMemoizationWStableCapture\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMemoizationWStableCapture\133useFir = true\135.txt"
@@ -27,14 +27,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Wrapper(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           println(param)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleParamInputs\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleParamInputs\133useFir = false\135.txt"
index 337a026..e343431 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleParamInputs\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleParamInputs\133useFir = false\135.txt"
@@ -24,8 +24,7 @@
 
 @Composable
 fun <T> loadResourceInternal(key: String, pendingResource: T?, failedResource: T?, %composer: Composer?, %changed: Int, %default: Int): Boolean {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(loadResourceInternal)P(1,2)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(loadResourceInternal)P(1,2)<rememb...>:Test.kt")
   if (%default and 0b0010 != 0) {
     pendingResource = null
   }
@@ -36,18 +35,17 @@
     traceEventStart(<>, %changed, -1, <>)
   }
   val deferred = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(key) || %changed and 0b0110 == 0b0100 or %changed and 0b01110000 xor 0b00110000 > 32 && %composer.changed(pendingResource) || %changed and 0b00110000 == 0b00100000 or %changed and 0b001110000000 xor 0b000110000000 > 256 && %composer.changed(failedResource) || %changed and 0b000110000000 == 0b000100000000) {
       123
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }
   val tmp0 = deferred > 10
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleParamInputs\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleParamInputs\133useFir = true\135.txt"
index 337a026..e343431 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleParamInputs\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleParamInputs\133useFir = true\135.txt"
@@ -24,8 +24,7 @@
 
 @Composable
 fun <T> loadResourceInternal(key: String, pendingResource: T?, failedResource: T?, %composer: Composer?, %changed: Int, %default: Int): Boolean {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(loadResourceInternal)P(1,2)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(loadResourceInternal)P(1,2)<rememb...>:Test.kt")
   if (%default and 0b0010 != 0) {
     pendingResource = null
   }
@@ -36,18 +35,17 @@
     traceEventStart(<>, %changed, -1, <>)
   }
   val deferred = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(key) || %changed and 0b0110 == 0b0100 or %changed and 0b01110000 xor 0b00110000 > 32 && %composer.changed(pendingResource) || %changed and 0b00110000 == 0b00100000 or %changed and 0b001110000000 xor 0b000110000000 > 256 && %composer.changed(failedResource) || %changed and 0b000110000000 == 0b000100000000) {
       123
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }
   val tmp0 = deferred > 10
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleRememberCallsInARow\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleRememberCallsInARow\133useFir = false\135.txt"
index 0c68513..2b69e35 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleRememberCallsInARow\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleRememberCallsInARow\133useFir = false\135.txt"
@@ -31,23 +31,21 @@
     val a = someInt()
     val b = someInt()
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%composer.changed(a) or %composer.changed(b)) {
         Foo(a, b)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val c = someInt()
     val d = someInt()
     val bar = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(%composer.changed(c) or %composer.changed(d)) {
         Foo(c, d)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleRememberCallsInARow\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleRememberCallsInARow\133useFir = true\135.txt"
index 0c68513..2b69e35 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleRememberCallsInARow\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testMultipleRememberCallsInARow\133useFir = true\135.txt"
@@ -31,23 +31,21 @@
     val a = someInt()
     val b = someInt()
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%composer.changed(a) or %composer.changed(b)) {
         Foo(a, b)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val c = someInt()
     val d = someInt()
     val bar = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(%composer.changed(c) or %composer.changed(d)) {
         Foo(c, d)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNoArgs\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNoArgs\133useFir = false\135.txt"
index 18a3294..662a08ff 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNoArgs\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNoArgs\133useFir = false\135.txt"
@@ -27,31 +27,28 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val bar = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     A(%composer, 0)
     val bam = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp2_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp2_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNoArgs\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNoArgs\133useFir = true\135.txt"
index 18a3294..662a08ff 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNoArgs\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNoArgs\133useFir = true\135.txt"
@@ -27,31 +27,28 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     val bar = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     A(%composer, 0)
     val bam = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp2_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp2_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonArgs\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonArgs\133useFir = false\135.txt"
index 0d4fc5c..55d5b38 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonArgs\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonArgs\133useFir = false\135.txt"
@@ -28,12 +28,11 @@
     val a = someInt()
     val b = someInt()
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%composer.changed(a) or %composer.changed(b)) {
         Foo(a, b)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonArgs\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonArgs\133useFir = true\135.txt"
index 0d4fc5c..55d5b38 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonArgs\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonArgs\133useFir = true\135.txt"
@@ -28,12 +28,11 @@
     val a = someInt()
     val b = someInt()
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%composer.changed(a) or %composer.changed(b)) {
         Foo(a, b)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonRestartableParameterInputsStableUnstableUncertain\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonRestartableParameterInputsStableUnstableUncertain\133useFir = false\135.txt"
index daf9eef..db9d416 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonRestartableParameterInputsStableUnstableUncertain\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonRestartableParameterInputsStableUnstableUncertain\133useFir = false\135.txt"
@@ -31,60 +31,54 @@
 @Composable
 @NonRestartableComposable
 fun test1(x: KnownStable, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(test1)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(test1)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(x) || %changed and 0b0110 == 0b0100) {
     1
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @Composable
 @NonRestartableComposable
 fun test2(x: KnownUnstable, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(test2)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(test2)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp0_group = %composer.cache(%composer.changed(x)) {
     1
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @Composable
 @NonRestartableComposable
 fun test3(x: Uncertain, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(test3)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(test3)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(x) || %changed and 0b0110 == 0b0100) {
     1
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonRestartableParameterInputsStableUnstableUncertain\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonRestartableParameterInputsStableUnstableUncertain\133useFir = true\135.txt"
index daf9eef..db9d416 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonRestartableParameterInputsStableUnstableUncertain\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testNonRestartableParameterInputsStableUnstableUncertain\133useFir = true\135.txt"
@@ -31,60 +31,54 @@
 @Composable
 @NonRestartableComposable
 fun test1(x: KnownStable, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(test1)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(test1)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(x) || %changed and 0b0110 == 0b0100) {
     1
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @Composable
 @NonRestartableComposable
 fun test2(x: KnownUnstable, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(test2)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(test2)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp0_group = %composer.cache(%composer.changed(x)) {
     1
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @Composable
 @NonRestartableComposable
 fun test3(x: Uncertain, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(test3)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(test3)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(x) || %changed and 0b0110 == 0b0100) {
     1
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testOptimizationFailsIfDefaultsGroupIsUsed\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testOptimizationFailsIfDefaultsGroupIsUsed\133useFir = false\135.txt"
index 80d8ad7..a167475 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testOptimizationFailsIfDefaultsGroupIsUsed\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testOptimizationFailsIfDefaultsGroupIsUsed\133useFir = false\135.txt"
@@ -43,12 +43,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     used(foo)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testOptimizationFailsIfDefaultsGroupIsUsed\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testOptimizationFailsIfDefaultsGroupIsUsed\133useFir = true\135.txt"
index 80d8ad7..a167475 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testOptimizationFailsIfDefaultsGroupIsUsed\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testOptimizationFailsIfDefaultsGroupIsUsed\133useFir = true\135.txt"
@@ -43,12 +43,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     used(foo)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInDirectFunction\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInDirectFunction\133useFir = false\135.txt"
index 0b232f9..5444059 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInDirectFunction\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInDirectFunction\133useFir = false\135.txt"
@@ -18,24 +18,22 @@
 
 @Composable
 fun Test(a: Int, %composer: Composer?, %changed: Int): Foo {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val b = someInt()
   val tmp0 = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(a) || %changed and 0b0110 == 0b0100 or %composer.changed(b)) {
       Foo(a, b)
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInDirectFunction\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInDirectFunction\133useFir = true\135.txt"
index 0b232f9..5444059 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInDirectFunction\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInDirectFunction\133useFir = true\135.txt"
@@ -18,24 +18,22 @@
 
 @Composable
 fun Test(a: Int, %composer: Composer?, %changed: Int): Foo {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(Test)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val b = someInt()
   val tmp0 = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(a) || %changed and 0b0110 == 0b0100 or %composer.changed(b)) {
       Foo(a, b)
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInRestartableFunction\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInRestartableFunction\133useFir = false\135.txt"
index 2cdb2f9..62e183f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInRestartableFunction\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInRestartableFunction\133useFir = false\135.txt"
@@ -30,12 +30,11 @@
     }
     val b = someInt()
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %composer.changed(b)) {
         Foo(a, b)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInRestartableFunction\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInRestartableFunction\133useFir = true\135.txt"
index 2cdb2f9..62e183f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInRestartableFunction\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testParamAndNonParamInputsInRestartableFunction\133useFir = true\135.txt"
@@ -30,12 +30,11 @@
     }
     val b = someInt()
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %composer.changed(b)) {
         Foo(a, b)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testPassedArgs\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testPassedArgs\133useFir = false\135.txt"
index 6a26590..24ef7b0 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testPassedArgs\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testPassedArgs\133useFir = false\135.txt"
@@ -15,23 +15,21 @@
 
 @Composable
 fun rememberFoo(a: Int, b: Int, %composer: Composer?, %changed: Int): Foo {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(rememberFoo)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(rememberFoo)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(a) || %changed and 0b0110 == 0b0100 or %changed and 0b01110000 xor 0b00110000 > 32 && %composer.changed(b) || %changed and 0b00110000 == 0b00100000) {
       Foo(a, b)
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testPassedArgs\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testPassedArgs\133useFir = true\135.txt"
index 6a26590..24ef7b0 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testPassedArgs\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testPassedArgs\133useFir = true\135.txt"
@@ -15,23 +15,21 @@
 
 @Composable
 fun rememberFoo(a: Int, b: Int, %composer: Composer?, %changed: Int): Foo {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(rememberFoo)<rememb...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "C(rememberFoo)<rememb...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp1_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(a) || %changed and 0b0110 == 0b0100 or %changed and 0b01110000 xor 0b00110000 > 32 && %composer.changed(b) || %changed and 0b00110000 == 0b00100000) {
       Foo(a, b)
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp1_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   return tmp0
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAdaptedFunctionReference\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAdaptedFunctionReference\133useFir = false\135.txt"
index e276214..f870e9f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAdaptedFunctionReference\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAdaptedFunctionReference\133useFir = false\135.txt"
@@ -28,15 +28,14 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100, <block>{
         fun effect(): Int {
           effect()
         }
         ::effect
       })
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     })
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAdaptedFunctionReference\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAdaptedFunctionReference\133useFir = true\135.txt"
index e276214..f870e9f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAdaptedFunctionReference\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAdaptedFunctionReference\133useFir = true\135.txt"
@@ -28,15 +28,14 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100, <block>{
         fun effect(): Int {
           effect()
         }
         ::effect
       })
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     })
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterNonStaticDefaultParameters\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterNonStaticDefaultParameters\133useFir = false\135.txt"
index f23cfaa..e99a45c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterNonStaticDefaultParameters\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterNonStaticDefaultParameters\133useFir = false\135.txt"
@@ -58,12 +58,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val s = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 == 0b00100000 or %dirty and 0b001110000000 xor 0b000110000000 > 256 && %composer.changed(c) || %dirty and 0b000110000000 == 0b000100000000) {
         Any()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     used(s, %composer, 0b1000)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterNonStaticDefaultParameters\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterNonStaticDefaultParameters\133useFir = true\135.txt"
index f23cfaa..e99a45c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterNonStaticDefaultParameters\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterNonStaticDefaultParameters\133useFir = true\135.txt"
@@ -58,12 +58,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val s = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 == 0b00100000 or %dirty and 0b001110000000 xor 0b000110000000 > 256 && %composer.changed(c) || %dirty and 0b000110000000 == 0b000100000000) {
         Any()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     used(s, %composer, 0b1000)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterStaticDefaultParameters\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterStaticDefaultParameters\133useFir = false\135.txt"
index edab87d..5469d90 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterStaticDefaultParameters\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterStaticDefaultParameters\133useFir = false\135.txt"
@@ -50,12 +50,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val s = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 == 0b00100000 or %dirty and 0b001110000000 == 0b000100000000) {
         Any()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     used(s, %composer, 0b1000)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterStaticDefaultParameters\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterStaticDefaultParameters\133useFir = true\135.txt"
index edab87d..5469d90 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterStaticDefaultParameters\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberAfterStaticDefaultParameters\133useFir = true\135.txt"
@@ -50,12 +50,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val s = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 == 0b00100000 or %dirty and 0b001110000000 == 0b000100000000) {
         Any()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     used(s, %composer, 0b1000)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = false\135.txt"
index d3280e7..a8e3e86 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = false\135.txt"
@@ -26,12 +26,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val a = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         param
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     Test(a, %composer, 0b0110)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = true\135.txt"
index d3280e7..a8e3e86 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = true\135.txt"
@@ -26,12 +26,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val a = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         param
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     Test(a, %composer, 0b0110)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberFunctionReference\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberFunctionReference\133useFir = false\135.txt"
index 3221580..ef91377 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberFunctionReference\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberFunctionReference\133useFir = false\135.txt"
@@ -28,10 +28,9 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100, ::effect)
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     })
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberFunctionReference\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberFunctionReference\133useFir = true\135.txt"
index 3221580..ef91377 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberFunctionReference\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberFunctionReference\133useFir = true\135.txt"
@@ -28,10 +28,9 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100, ::effect)
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     })
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop\133useFir = false\135.txt"
index 9680ed7..ed9888e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop\133useFir = false\135.txt"
@@ -28,23 +28,21 @@
     while (<iterator>.hasNext()) {
       val index = <iterator>.next()
       val i = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           index
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
     }
     %composer.endReplaceableGroup()
     val a = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(false) {
         1
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop\133useFir = true\135.txt"
index be1c5ba..eeb8395 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop\133useFir = true\135.txt"
@@ -28,23 +28,21 @@
     while (<iterator>.hasNext()) {
       val index = <iterator>.next()
       val i = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           index
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
     }
     %composer.endReplaceableGroup()
     val a = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp1_group = %composer.cache(false) {
         1
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp1_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop_NoTrailingRemember\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop_NoTrailingRemember\133useFir = false\135.txt"
index ab9341b..0284347 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop_NoTrailingRemember\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop_NoTrailingRemember\133useFir = false\135.txt"
@@ -25,12 +25,11 @@
     while (<iterator>.hasNext()) {
       val index = <iterator>.next()
       val i = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           index
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop_NoTrailingRemember\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop_NoTrailingRemember\133useFir = true\135.txt"
index af524b9..42ea03a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop_NoTrailingRemember\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInALoop_NoTrailingRemember\133useFir = true\135.txt"
@@ -25,12 +25,11 @@
     while (<iterator>.hasNext()) {
       val index = <iterator>.next()
       val i = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           index
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIfWithComposableCallBefore\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIfWithComposableCallBefore\133useFir = false\135.txt"
index 0b3fa5b..dc9e12c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIfWithComposableCallBefore\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIfWithComposableCallBefore\133useFir = false\135.txt"
@@ -33,12 +33,11 @@
     if (condition) {
       A(%composer, 0)
       val foo = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           Foo()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIfWithComposableCallBefore\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIfWithComposableCallBefore\133useFir = true\135.txt"
index 0b3fa5b..dc9e12c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIfWithComposableCallBefore\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIfWithComposableCallBefore\133useFir = true\135.txt"
@@ -33,12 +33,11 @@
     if (condition) {
       A(%composer, 0)
       val foo = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           Foo()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIf\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIf\133useFir = false\135.txt"
index 1c02736..de20693 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIf\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIf\133useFir = false\135.txt"
@@ -33,12 +33,11 @@
     A(%composer, 0)
     if (condition) {
       val foo = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           Foo()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIf\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIf\133useFir = true\135.txt"
index 1c02736..de20693 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIf\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfIf\133useFir = true\135.txt"
@@ -33,12 +33,11 @@
     A(%composer, 0)
     if (condition) {
       val foo = <block>{
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(false) {
           Foo()
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithCallsAfter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithCallsAfter\133useFir = false\135.txt"
index 9cdeeb1..e235a45 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithCallsAfter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithCallsAfter\133useFir = false\135.txt"
@@ -31,12 +31,11 @@
   while (<iterator>.hasNext()) {
     val item = <iterator>.next()
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     A(%composer, 0)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithCallsAfter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithCallsAfter\133useFir = true\135.txt"
index 9cdeeb1..e235a45 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithCallsAfter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithCallsAfter\133useFir = true\135.txt"
@@ -31,12 +31,11 @@
   while (<iterator>.hasNext()) {
     val item = <iterator>.next()
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     A(%composer, 0)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithOnlyRemembers\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithOnlyRemembers\133useFir = false\135.txt"
index 1eb390d..3005a81 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithOnlyRemembers\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithOnlyRemembers\133useFir = false\135.txt"
@@ -30,12 +30,11 @@
   while (<iterator>.hasNext()) {
     val item = <iterator>.next()
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     print(foo)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithOnlyRemembers\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithOnlyRemembers\133useFir = true\135.txt"
index 1eb390d..3005a81 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithOnlyRemembers\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberInsideOfWhileWithOnlyRemembers\133useFir = true\135.txt"
@@ -30,12 +30,11 @@
   while (<iterator>.hasNext()) {
     val item = <iterator>.next()
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     print(foo)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberMemoizedLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberMemoizedLambda\133useFir = false\135.txt"
index 25e03f8..e1fe07d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberMemoizedLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberMemoizedLambda\133useFir = false\135.txt"
@@ -28,14 +28,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           a
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     })
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberMemoizedLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberMemoizedLambda\133useFir = true\135.txt"
index 25e03f8..e1fe07d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberMemoizedLambda\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberMemoizedLambda\133useFir = true\135.txt"
@@ -28,14 +28,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           a
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     })
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberPropertyReference\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberPropertyReference\133useFir = false\135.txt"
index 742f51e..57bbdc8 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberPropertyReference\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberPropertyReference\133useFir = false\135.txt"
@@ -28,10 +28,9 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100, a::value)
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     })
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberPropertyReference\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberPropertyReference\133useFir = true\135.txt"
index 742f51e..57bbdc8 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberPropertyReference\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberPropertyReference\133useFir = true\135.txt"
@@ -28,10 +28,9 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     used(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100, a::value)
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     })
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithInlineClassInput\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithInlineClassInput\133useFir = false\135.txt"
index c684450..f48a3b7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithInlineClassInput\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithInlineClassInput\133useFir = false\135.txt"
@@ -30,12 +30,11 @@
     }
     val a = InlineInt(123)
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithInlineClassInput\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithInlineClassInput\133useFir = true\135.txt"
index c684450..f48a3b7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithInlineClassInput\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithInlineClassInput\133useFir = true\135.txt"
@@ -30,12 +30,11 @@
     }
     val a = InlineInt(123)
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithNArgs\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithNArgs\133useFir = false\135.txt"
index bba1bc87d..13525f3 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithNArgs\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithNArgs\133useFir = false\135.txt"
@@ -37,12 +37,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 == 0b00100000 or %dirty and 0b001110000000 == 0b000100000000 or %dirty and 0b0001110000000000 == 0b100000000000) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithNArgs\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithNArgs\133useFir = true\135.txt"
index bba1bc87d..13525f3 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithNArgs\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithNArgs\133useFir = true\135.txt"
@@ -37,12 +37,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val foo = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 == 0b00100000 or %dirty and 0b001110000000 == 0b000100000000 or %dirty and 0b0001110000000000 == 0b100000000000) {
         Foo()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstableUnused_InInlineLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstableUnused_InInlineLambda\133useFir = false\135.txt"
index 899f7c7..e0489b7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstableUnused_InInlineLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstableUnused_InInlineLambda\133useFir = false\135.txt"
@@ -28,12 +28,11 @@
     }
     InlineWrapper({ %composer: Composer?, %changed: Int ->
       sourceInformationMarkerStart(%composer, <>, "C<rememb...>:Test.kt")
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       %composer.cache(%dirty and 0b1110 == 0b0100) {
         param
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       sourceInformationMarkerEnd(%composer)
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstableUnused_InInlineLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstableUnused_InInlineLambda\133useFir = true\135.txt"
index 899f7c7..e0489b7 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstableUnused_InInlineLambda\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstableUnused_InInlineLambda\133useFir = true\135.txt"
@@ -28,12 +28,11 @@
     }
     InlineWrapper({ %composer: Composer?, %changed: Int ->
       sourceInformationMarkerStart(%composer, <>, "C<rememb...>:Test.kt")
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       %composer.cache(%dirty and 0b1110 == 0b0100) {
         param
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       sourceInformationMarkerEnd(%composer)
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_InInlineLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_InInlineLambda\133useFir = false\135.txt"
index 0fc248d..d8f0c08 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_InInlineLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_InInlineLambda\133useFir = false\135.txt"
@@ -26,12 +26,11 @@
   println(unstable)
   InlineWrapper({ %composer: Composer?, %changed: Int ->
     sourceInformationMarkerStart(%composer, <>, "C<rememb...>:Test.kt")
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     %composer.cache(%changed@Test and 0b1110 xor 0b0110 > 4 && %composer@Test.changed(param) || %changed@Test and 0b0110 == 0b0100) {
       param
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     sourceInformationMarkerEnd(%composer)
   }, %composer, 0)
   if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_InInlineLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_InInlineLambda\133useFir = true\135.txt"
index 0fc248d..d8f0c08 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_InInlineLambda\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_InInlineLambda\133useFir = true\135.txt"
@@ -26,12 +26,11 @@
   println(unstable)
   InlineWrapper({ %composer: Composer?, %changed: Int ->
     sourceInformationMarkerStart(%composer, <>, "C<rememb...>:Test.kt")
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     %composer.cache(%changed@Test and 0b1110 xor 0b0110 > 4 && %composer@Test.changed(param) || %changed@Test and 0b0110 == 0b0100) {
       param
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     sourceInformationMarkerEnd(%composer)
   }, %composer, 0)
   if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_inLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_inLambda\133useFir = false\135.txt"
index 92cd310..11c45ac 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_inLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_inLambda\133useFir = false\135.txt"
@@ -17,29 +17,28 @@
 @Composable
 fun Test(param: String, unstable: List<*>, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)<Wrappe...>:Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,<Wrappe...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  Wrapper(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+  Wrapper(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
     sourceInformation(%composer, "C<rememb...>:Test.kt")
     if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       %composer.cache(%composer.changed(param) or %composer.changed(unstable)) {
         param
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       if (isTraceInProgress()) {
         traceEventEnd()
       }
     } else {
       %composer.skipToGroupEnd()
     }
-  }, %composer, 0b0110)
+  }, %composer, 0b00110110), %composer, 0b0110)
   if (isTraceInProgress()) {
     traceEventEnd()
   }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_inLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_inLambda\133useFir = true\135.txt"
index 92cd310..11c45ac 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_inLambda\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberWithUnstable_inLambda\133useFir = true\135.txt"
@@ -17,29 +17,28 @@
 @Composable
 fun Test(param: String, unstable: List<*>, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)<Wrappe...>:Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,<Wrappe...>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  Wrapper(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+  Wrapper(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
     sourceInformation(%composer, "C<rememb...>:Test.kt")
     if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
       if (isTraceInProgress()) {
         traceEventStart(<>, %changed, -1, <>)
       }
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       %composer.cache(%composer.changed(param) or %composer.changed(unstable)) {
         param
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       if (isTraceInProgress()) {
         traceEventEnd()
       }
     } else {
       %composer.skipToGroupEnd()
     }
-  }, %composer, 0b0110)
+  }, %composer, 0b00110110), %composer, 0b0110)
   if (isTraceInProgress()) {
     traceEventEnd()
   }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRestartableParameterInputsStableUnstableUncertain\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRestartableParameterInputsStableUnstableUncertain\133useFir = false\135.txt"
index 761697c..b920837 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRestartableParameterInputsStableUnstableUncertain\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRestartableParameterInputsStableUnstableUncertain\133useFir = false\135.txt"
@@ -35,12 +35,11 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
       1
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
     if (isTraceInProgress()) {
       traceEventEnd()
@@ -59,12 +58,11 @@
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp0_group = %composer.cache(%composer.changed(x)) {
     1
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
@@ -85,12 +83,11 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 || %dirty and 0b1000 != 0 && %composer.changed(x)) {
       1
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRestartableParameterInputsStableUnstableUncertain\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRestartableParameterInputsStableUnstableUncertain\133useFir = true\135.txt"
index 761697c..b920837 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRestartableParameterInputsStableUnstableUncertain\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRestartableParameterInputsStableUnstableUncertain\133useFir = true\135.txt"
@@ -35,12 +35,11 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
       1
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
     if (isTraceInProgress()) {
       traceEventEnd()
@@ -59,12 +58,11 @@
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
   val tmp0_group = %composer.cache(%composer.changed(x)) {
     1
   }
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
   tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
@@ -85,12 +83,11 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 || %dirty and 0b1000 != 0 && %composer.changed(x)) {
       1
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testVarargsIntrinsicRemember\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testVarargsIntrinsicRemember\133useFir = false\135.txt"
index ef9eb44..ac97240 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testVarargsIntrinsicRemember\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testVarargsIntrinsicRemember\133useFir = false\135.txt"
@@ -36,14 +36,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val show = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         mutableStateOf(
           value = false
         )
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (show.value) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testVarargsIntrinsicRemember\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testVarargsIntrinsicRemember\133useFir = true\135.txt"
index ef9eb44..ac97240 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testVarargsIntrinsicRemember\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testVarargsIntrinsicRemember\133useFir = true\135.txt"
@@ -36,14 +36,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val show = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         mutableStateOf(
           value = false
         )
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }
     if (show.value) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testZeroArgRemember\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testZeroArgRemember\133useFir = false\135.txt"
index 38fe49f..9043caa 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testZeroArgRemember\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testZeroArgRemember\133useFir = false\135.txt"
@@ -24,12 +24,11 @@
     traceEventStart(<>, %changed, -1, <>)
   }
   val foo = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(false) {
       Foo()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
   }
   used(items)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testZeroArgRemember\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testZeroArgRemember\133useFir = true\135.txt"
index 38fe49f..9043caa 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testZeroArgRemember\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testZeroArgRemember\133useFir = true\135.txt"
@@ -24,12 +24,11 @@
     traceEventStart(<>, %changed, -1, <>)
   }
   val foo = <block>{
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(false) {
       Foo()
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
   }
   used(items)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWStableCapture\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWStableCapture\133useFir = false\135.txt"
index ea4d3d7..cd6bd78 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWStableCapture\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWStableCapture\133useFir = false\135.txt"
@@ -27,14 +27,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Wrapper(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           println(param)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWStableCapture\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWStableCapture\133useFir = true\135.txt"
index ea4d3d7..cd6bd78 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWStableCapture\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWStableCapture\133useFir = true\135.txt"
@@ -27,14 +27,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Wrapper(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           println(param)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWUnstableCapture\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWUnstableCapture\133useFir = false\135.txt"
index 06dfaac..c931541 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWUnstableCapture\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWUnstableCapture\133useFir = false\135.txt"
@@ -27,14 +27,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Wrapper(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%composer.changedInstance(unstable)) {
         {
           println(unstable)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWUnstableCapture\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWUnstableCapture\133useFir = true\135.txt"
index 06dfaac..c931541 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWUnstableCapture\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testMemoizationWUnstableCapture\133useFir = true\135.txt"
@@ -27,14 +27,13 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Wrapper(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(%composer.changedInstance(unstable)) {
         {
           println(unstable)
         }
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberMethodReference\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberMethodReference\133useFir = false\135.txt"
index 61e681f..353e2a3 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberMethodReference\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberMethodReference\133useFir = false\135.txt"
@@ -29,20 +29,18 @@
     val remembered = <block>{
       val tmp1_remember%arg%0 = <block>{
         val tmp0 = param
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
           tmp0::toString
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       %composer.cache(%composer.changed(tmp1_remember%arg%0)) {
         TODO()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberMethodReference\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberMethodReference\133useFir = true\135.txt"
index 83c066f..5446b7e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberMethodReference\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberMethodReference\133useFir = true\135.txt"
@@ -29,20 +29,18 @@
     val remembered = <block>{
       val tmp1_remember%arg%0 = <block>{
         val tmp0 = param
-        %composer.startReplaceableGroup(<>)
-        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
         val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
           tmp0::toString
         }
-        %composer.endReplaceableGroup()
+        sourceInformationMarkerEnd(%composer)
         tmp0_group
       }
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       %composer.cache(%composer.changed(tmp1_remember%arg%0)) {
         TODO()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithDefaultParams\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithDefaultParams\133useFir = false\135.txt"
index 760d0e4..696a4f6 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithDefaultParams\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithDefaultParams\133useFir = false\135.txt"
@@ -46,12 +46,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val remembered = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 xor 0b00110000 > 32 && %composer.changed(defaultParam) || %dirty and 0b00110000 == 0b00100000) {
         TODO()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithDefaultParams\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithDefaultParams\133useFir = true\135.txt"
index 760d0e4..696a4f6 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithDefaultParams\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithDefaultParams\133useFir = true\135.txt"
@@ -46,12 +46,11 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     val remembered = <block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 xor 0b00110000 > 32 && %composer.changed(defaultParam) || %dirty and 0b00110000 == 0b00100000) {
         TODO()
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithUnstableParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithUnstableParam\133useFir = false\135.txt"
index 9cc440b..9506e9b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithUnstableParam\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithUnstableParam\133useFir = false\135.txt"
@@ -26,12 +26,11 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(%composer.changed(unstable)) {
       unstable[0]
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithUnstableParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithUnstableParam\133useFir = true\135.txt"
index 9cc440b..9506e9b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithUnstableParam\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTestsStrongSkipping/testRememberWithUnstableParam\133useFir = true\135.txt"
@@ -26,12 +26,11 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    %composer.startReplaceableGroup(<>)
-    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
     val tmp0_group = %composer.cache(%composer.changed(unstable)) {
       unstable[0]
     }
-    %composer.endReplaceableGroup()
+    sourceInformationMarkerEnd(%composer)
     tmp0_group
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = false\135.txt"
index 9210ddc..4f4f9d4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = false\135.txt"
@@ -33,12 +33,11 @@
     A(x, %composer, 0b1110 and %dirty)
     A(Foo(0), %composer, 0)
     A(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo(0)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0b0110)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = true\135.txt"
index 9210ddc..4f4f9d4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = true\135.txt"
@@ -33,12 +33,11 @@
     A(x, %composer, 0b1110 and %dirty)
     A(Foo(0), %composer, 0)
     A(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo(0)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0b0110)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = false\135.txt"
index b89201a..c830931 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = false\135.txt"
@@ -33,12 +33,11 @@
     A(x, %composer, 0b1110 and %dirty)
     A(Foo(0), %composer, 0)
     A(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo(0)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = true\135.txt"
index b89201a..c830931 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = true\135.txt"
@@ -33,12 +33,11 @@
     A(x, %composer, 0b1110 and %dirty)
     A(Foo(0), %composer, 0)
     A(<block>{
-      %composer.startReplaceableGroup(<>)
-      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      sourceInformationMarkerStart(%composer, <>, "CC(remember):Test.kt#9igjgp")
       val tmp0_group = %composer.cache(false) {
         Foo(0)
       }
-      %composer.endReplaceableGroup()
+      sourceInformationMarkerEnd(%composer)
       tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = false\135.txt"
index 45de354..fb1a247 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = false\135.txt"
@@ -52,15 +52,10 @@
     TestMemoizedFun(TestFunInterface { it: Int ->
       use(it)
     }, %composer, 0b0110)
-    TestMemoizedFun(<block>{
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(capture)) {
-        TestFunInterface { it: Int ->
-          use(capture)
-        }
+    TestMemoizedFun(%composer.cache(%composer.changed(capture)) {
+      TestFunInterface { it: Int ->
+        use(capture)
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = true\135.txt"
index be475f6..192c84b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = true\135.txt"
@@ -52,15 +52,10 @@
     TestMemoizedFun(TestFunInterface { it: Int ->
       use(it)
     }, %composer, 0b0110)
-    TestMemoizedFun(<block>{
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(false) {
-        TestFunInterface { it: Int ->
-          use(capture)
-        }
+    TestMemoizedFun(%composer.cache(false) {
+      TestFunInterface { it: Int ->
+        use(capture)
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = false\135.txt"
index 45de354..fb1a247 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = false\135.txt"
@@ -52,15 +52,10 @@
     TestMemoizedFun(TestFunInterface { it: Int ->
       use(it)
     }, %composer, 0b0110)
-    TestMemoizedFun(<block>{
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(capture)) {
-        TestFunInterface { it: Int ->
-          use(capture)
-        }
+    TestMemoizedFun(%composer.cache(%composer.changed(capture)) {
+      TestFunInterface { it: Int ->
+        use(capture)
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = true\135.txt"
index be475f6..192c84b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = true\135.txt"
@@ -52,15 +52,10 @@
     TestMemoizedFun(TestFunInterface { it: Int ->
       use(it)
     }, %composer, 0b0110)
-    TestMemoizedFun(<block>{
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(false) {
-        TestFunInterface { it: Int ->
-          use(capture)
-        }
+    TestMemoizedFun(%composer.cache(false) {
+      TestFunInterface { it: Int ->
+        use(capture)
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = false\135.txt"
index 536a6d3..5f200f9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = false\135.txt"
@@ -33,7 +33,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val lambda = composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    val lambda = rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
           traceEventStart(<>, %changed, -1, <>)
@@ -46,7 +46,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }
+    }, %composer, 0b00110110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = true\135.txt"
index 536a6d3..5f200f9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = true\135.txt"
@@ -33,7 +33,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val lambda = composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    val lambda = rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
           traceEventStart(<>, %changed, -1, <>)
@@ -46,7 +46,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }
+    }, %composer, 0b00110110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = false\135.txt"
index 536a6d3..5f200f9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = false\135.txt"
@@ -33,7 +33,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val lambda = composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    val lambda = rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
           traceEventStart(<>, %changed, -1, <>)
@@ -46,7 +46,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }
+    }, %composer, 0b00110110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = true\135.txt"
index 536a6d3..5f200f9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = true\135.txt"
@@ -33,7 +33,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val lambda = composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    val lambda = rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
           traceEventStart(<>, %changed, -1, <>)
@@ -46,7 +46,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }
+    }, %composer, 0b00110110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
index 994ca87..de49704 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
@@ -30,16 +30,11 @@
     }
     val foo = Foo(0)
     val bar = Bar(1)
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
-        {
-          foo
-          bar
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+      {
+        foo
+        bar
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt"
index 34d44a4..de49704 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt"
@@ -30,16 +30,11 @@
     }
     val foo = Foo(0)
     val bar = Bar(1)
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
-        {
-          foo
-          bar
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+      {
+        foo
+        bar
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
index 994ca87..de49704 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
@@ -30,16 +30,11 @@
     }
     val foo = Foo(0)
     val bar = Bar(1)
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
-        {
-          foo
-          bar
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+      {
+        foo
+        bar
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt"
index 34d44a4..de49704 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt"
@@ -30,16 +30,11 @@
     }
     val foo = Foo(0)
     val bar = Bar(1)
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
-        {
-          foo
-          bar
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+      {
+        foo
+        bar
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
index a399ea5..6b42750 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
@@ -25,15 +25,10 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val foo = Foo(0)
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo)) {
-        {
-          foo
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo)) {
+      {
+        foo
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt"
index 1684495..6b42750 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt"
@@ -25,15 +25,10 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val foo = Foo(0)
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(%composer.changedInstance(foo)) {
-        {
-          foo
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo)) {
+      {
+        foo
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
index a399ea5..6b42750 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
@@ -25,15 +25,10 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val foo = Foo(0)
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo)) {
-        {
-          foo
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo)) {
+      {
+        foo
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt"
index 1684495..6b42750 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt"
@@ -25,15 +25,10 @@
       traceEventStart(<>, %changed, -1, <>)
     }
     val foo = Foo(0)
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(%composer.changedInstance(foo)) {
-        {
-          foo
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo)) {
+      {
+        foo
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = false\135.txt"
index ddced50..3ab4202 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = false\135.txt"
@@ -33,16 +33,11 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
-        {
-          foo
-          bar
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+      {
+        foo
+        bar
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = true\135.txt"
index ad46339..492a0db 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = true\135.txt"
@@ -33,16 +33,11 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %dirty and 0b01110000 == 0b00100000) {
-        {
-          foo
-          bar
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo) or %dirty and 0b01110000 == 0b00100000) {
+      {
+        foo
+        bar
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = false\135.txt"
index ddced50..3ab4202 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = false\135.txt"
@@ -33,16 +33,11 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
-        {
-          foo
-          bar
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+      {
+        foo
+        bar
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = true\135.txt"
index ad46339..492a0db 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = true\135.txt"
@@ -33,16 +33,11 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val lambda = <block>{
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %dirty and 0b01110000 == 0b00100000) {
-        {
-          foo
-          bar
-        }
+    val lambda = %composer.cache(%composer.changedInstance(foo) or %dirty and 0b01110000 == 0b00100000) {
+      {
+        foo
+        bar
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
index 046e728..8d5e339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
@@ -25,12 +25,9 @@
     }
     val x = <block>{
       val tmp0 = unstable
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
+      %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt"
index 32f8d7f..8d5e339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt"
@@ -25,12 +25,9 @@
     }
     val x = <block>{
       val tmp0 = unstable
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
+      %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
index 046e728..8d5e339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
@@ -25,12 +25,9 @@
     }
     val x = <block>{
       val tmp0 = unstable
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
+      %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt"
index 32f8d7f..8d5e339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt"
@@ -25,12 +25,9 @@
     }
     val x = <block>{
       val tmp0 = unstable
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
+      %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
index 046e728..8d5e339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
@@ -25,12 +25,9 @@
     }
     val x = <block>{
       val tmp0 = unstable
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
+      %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt"
index 32f8d7f..8d5e339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt"
@@ -25,12 +25,9 @@
     }
     val x = <block>{
       val tmp0 = unstable
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
+      %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
index 046e728..8d5e339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
@@ -25,12 +25,9 @@
     }
     val x = <block>{
       val tmp0 = unstable
-      %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
+      %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
-      %composer.endReplaceableGroup()
-      tmpCache
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt"
index 32f8d7f..8d5e339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt"
@@ -25,12 +25,9 @@
     }
     val x = <block>{
       val tmp0 = unstable
-      %composer.startReplaceableGroup(<>)
-      val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
+      %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
-      %composer.endReplaceableGroup()
-      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testCallingLayout\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testCallingLayout\133useFir = false\135.txt"
index c35f40b..22da6d8 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testCallingLayout\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testCallingLayout\133useFir = false\135.txt"
@@ -154,7 +154,7 @@
 @ComposableInferredTarget(scheme = "[androidx.compose.ui.UiComposable[androidx.compose.ui.UiComposable]]")
 fun Test5(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test5)<Compos...>:Test.kt")
+  sourceInformation(%composer, "C(Test5)<{>,<Compos...>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b0110 == 0) {
     %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
@@ -163,7 +163,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    CompositionLocalProvider(Local provides 5, composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    CompositionLocalProvider(Local provides 5, rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<Test1(...>,<conten...>:Test.kt")
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -177,7 +177,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b00110000 or ProvidedValue.%stable or 0)
+    }, %composer, 0b00110110), %composer, 0b00110000 or ProvidedValue.%stable)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
@@ -192,7 +192,7 @@
 @ComposableTarget(applier = "androidx.compose.ui.UiComposable")
 fun Test6(test: String, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test6)<Compos...>:Test.kt")
+  sourceInformation(%composer, "C(Test6)<{>,<Compos...>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b0110 == 0) {
     %dirty = %dirty or if (%composer.changed(test)) 0b0100 else 0b0010
@@ -201,7 +201,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    CompositionLocalProvider(Local provides 6, composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    CompositionLocalProvider(Local provides 6, rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<T(test...>,<Test1(...>:Test.kt")
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -215,7 +215,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b00110000 or ProvidedValue.%stable or 0)
+    }, %composer, 0b00110110), %composer, 0b00110000 or ProvidedValue.%stable)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testCallingLayout\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testCallingLayout\133useFir = true\135.txt"
index c35f40b..22da6d8 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testCallingLayout\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testCallingLayout\133useFir = true\135.txt"
@@ -154,7 +154,7 @@
 @ComposableInferredTarget(scheme = "[androidx.compose.ui.UiComposable[androidx.compose.ui.UiComposable]]")
 fun Test5(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test5)<Compos...>:Test.kt")
+  sourceInformation(%composer, "C(Test5)<{>,<Compos...>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b0110 == 0) {
     %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
@@ -163,7 +163,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    CompositionLocalProvider(Local provides 5, composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    CompositionLocalProvider(Local provides 5, rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<Test1(...>,<conten...>:Test.kt")
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -177,7 +177,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b00110000 or ProvidedValue.%stable or 0)
+    }, %composer, 0b00110110), %composer, 0b00110000 or ProvidedValue.%stable)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
@@ -192,7 +192,7 @@
 @ComposableTarget(applier = "androidx.compose.ui.UiComposable")
 fun Test6(test: String, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test6)<Compos...>:Test.kt")
+  sourceInformation(%composer, "C(Test6)<{>,<Compos...>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b0110 == 0) {
     %dirty = %dirty or if (%composer.changed(test)) 0b0100 else 0b0010
@@ -201,7 +201,7 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    CompositionLocalProvider(Local provides 6, composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+    CompositionLocalProvider(Local provides 6, rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
       sourceInformation(%composer, "C<T(test...>,<Test1(...>:Test.kt")
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
@@ -215,7 +215,7 @@
       } else {
         %composer.skipToGroupEnd()
       }
-    }, %composer, 0b00110000 or ProvidedValue.%stable or 0)
+    }, %composer, 0b00110110), %composer, 0b00110000 or ProvidedValue.%stable)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testOptionalParameters\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testOptionalParameters\133useFir = false\135.txt"
index 3b7e3a1..fad6262 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testOptionalParameters\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testOptionalParameters\133useFir = false\135.txt"
@@ -160,7 +160,19 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     one(%composer, 0b1110 and %dirty)
-    two?.invoke(%composer, 0b1110 and %dirty shr 0b0011)
+    val tmp0_safe_receiver = two
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "<invoke...>")
+    val tmp0_group = when {
+      tmp0_safe_receiver == null -> {
+        null
+      }
+      else -> {
+        tmp0_safe_receiver(%composer, 0b1110 and %dirty shr 0b0011)
+      }
+    }
+    %composer.endReplaceableGroup()
+    tmp0_group
     val tmp1_safe_receiver = three
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "*<it()>")
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testOptionalParameters\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testOptionalParameters\133useFir = true\135.txt"
index 3b7e3a1..fad6262 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testOptionalParameters\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testOptionalParameters\133useFir = true\135.txt"
@@ -160,7 +160,19 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     one(%composer, 0b1110 and %dirty)
-    two?.invoke(%composer, 0b1110 and %dirty shr 0b0011)
+    val tmp0_safe_receiver = two
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "<invoke...>")
+    val tmp0_group = when {
+      tmp0_safe_receiver == null -> {
+        null
+      }
+      else -> {
+        tmp0_safe_receiver(%composer, 0b1110 and %dirty shr 0b0011)
+      }
+    }
+    %composer.endReplaceableGroup()
+    tmp0_group
     val tmp1_safe_receiver = three
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "*<it()>")
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testReceiverScope\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testReceiverScope\133useFir = false\135.txt"
index cbf82fb..ccc06ba 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testReceiverScope\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testReceiverScope\133useFir = false\135.txt"
@@ -64,8 +64,7 @@
 @Composable
 @ComposableInferredTarget(scheme = "[androidx.compose.ui.UiComposable[androidx.compose.ui.UiComposable]]")
 fun LocalBox(modifier: Modifier?, content: @[ExtensionFunctionType] Function3<LocalBoxScope, Composer, Int, Unit>, %composer: Composer?, %changed: Int, %default: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(LocalBox)P(1)<Layout...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "CC(LocalBox)P(1)<Layout...>:Test.kt")
   if (%default and 0b0001 != 0) {
     modifier = Companion
   }
@@ -75,5 +74,5 @@
     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()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testReceiverScope\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testReceiverScope\133useFir = true\135.txt"
index cbf82fb..ccc06ba 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testReceiverScope\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testReceiverScope\133useFir = true\135.txt"
@@ -64,8 +64,7 @@
 @Composable
 @ComposableInferredTarget(scheme = "[androidx.compose.ui.UiComposable[androidx.compose.ui.UiComposable]]")
 fun LocalBox(modifier: Modifier?, content: @[ExtensionFunctionType] Function3<LocalBoxScope, Composer, Int, Unit>, %composer: Composer?, %changed: Int, %default: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(LocalBox)P(1)<Layout...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "CC(LocalBox)P(1)<Layout...>:Test.kt")
   if (%default and 0b0001 != 0) {
     modifier = Companion
   }
@@ -75,5 +74,5 @@
     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()
+  sourceInformationMarkerEnd(%composer)
 }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testRememberUpdatedState\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testRememberUpdatedState\133useFir = false\135.txt"
index 7dff6aa..4fc969c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testRememberUpdatedState\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testRememberUpdatedState\133useFir = false\135.txt"
@@ -22,7 +22,7 @@
 @ComposableInferredTarget(scheme = "[UI[UI]]")
 fun Test(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)<rememb...>:Test.kt")
+  sourceInformation(%composer, "C(Test)<rememb...>,<{>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b0110 == 0) {
     %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
@@ -37,13 +37,13 @@
         return updatedContent%delegate.getValue(null, ::updatedContent%delegate)
       }
     }
-    Defer(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
-      sourceInformation(%composer, "C:Test.kt")
+    Defer(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
+      sourceInformation(%composer, "C<{>:Test.kt")
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
           traceEventStart(<>, %changed, -1, <>)
         }
-        UiContent(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+        UiContent(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
           sourceInformation(%composer, "C<update...>:Test.kt")
           if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
             if (isTraceInProgress()) {
@@ -56,16 +56,14 @@
           } else {
             %composer.skipToGroupEnd()
           }
-        }
-        )
+        }, %composer, 0b00110110))
         if (isTraceInProgress()) {
           traceEventEnd()
         }
       } else {
         %composer.skipToGroupEnd()
       }
-    }
-    )
+    }, %composer, 0b00110110))
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testRememberUpdatedState\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testRememberUpdatedState\133useFir = true\135.txt"
index 7dff6aa..4fc969c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testRememberUpdatedState\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TargetAnnotationsTransformTests/testRememberUpdatedState\133useFir = true\135.txt"
@@ -22,7 +22,7 @@
 @ComposableInferredTarget(scheme = "[UI[UI]]")
 fun Test(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test)<rememb...>:Test.kt")
+  sourceInformation(%composer, "C(Test)<rememb...>,<{>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b0110 == 0) {
     %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
@@ -37,13 +37,13 @@
         return updatedContent%delegate.getValue(null, ::updatedContent%delegate)
       }
     }
-    Defer(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
-      sourceInformation(%composer, "C:Test.kt")
+    Defer(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
+      sourceInformation(%composer, "C<{>:Test.kt")
       if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
         if (isTraceInProgress()) {
           traceEventStart(<>, %changed, -1, <>)
         }
-        UiContent(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
+        UiContent(rememberComposableLambda(<>, true, { %composer: Composer?, %changed: Int ->
           sourceInformation(%composer, "C<update...>:Test.kt")
           if (%changed and 0b0011 != 0b0010 || !%composer.skipping) {
             if (isTraceInProgress()) {
@@ -56,16 +56,14 @@
           } else {
             %composer.skipToGroupEnd()
           }
-        }
-        )
+        }, %composer, 0b00110110))
         if (isTraceInProgress()) {
           traceEventEnd()
         }
       } else {
         %composer.skipToGroupEnd()
       }
-    }
-    )
+    }, %composer, 0b00110110))
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TraceInformationTest/testInlineFunctionsDonotGenerateTraceMarkers\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TraceInformationTest/testInlineFunctionsDonotGenerateTraceMarkers\133useFir = false\135.txt"
index 6fba851..73bb99b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TraceInformationTest/testInlineFunctionsDonotGenerateTraceMarkers\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TraceInformationTest/testInlineFunctionsDonotGenerateTraceMarkers\133useFir = false\135.txt"
@@ -25,10 +25,9 @@
 @Composable
 @ComposableInferredTarget(scheme = "[0[0]]")
 fun Wrapper(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(Wrapper)<conten...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "CC(Wrapper)<conten...>:Test.kt")
   content(%composer, 0b1110 and %changed)
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @Composable
 fun Test(condition: Boolean, %composer: Composer?, %changed: Int) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TraceInformationTest/testInlineFunctionsDonotGenerateTraceMarkers\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TraceInformationTest/testInlineFunctionsDonotGenerateTraceMarkers\133useFir = true\135.txt"
index 6fba851..73bb99b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TraceInformationTest/testInlineFunctionsDonotGenerateTraceMarkers\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.TraceInformationTest/testInlineFunctionsDonotGenerateTraceMarkers\133useFir = true\135.txt"
@@ -25,10 +25,9 @@
 @Composable
 @ComposableInferredTarget(scheme = "[0[0]]")
 fun Wrapper(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
-  %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "CC(Wrapper)<conten...>:Test.kt")
+  sourceInformationMarkerStart(%composer, <>, "CC(Wrapper)<conten...>:Test.kt")
   content(%composer, 0b1110 and %changed)
-  %composer.endReplaceableGroup()
+  sourceInformationMarkerEnd(%composer)
 }
 @Composable
 fun Test(condition: Boolean, %composer: Composer?, %changed: Int) {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt
index 001ab7e..0d3efcb 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt
@@ -85,6 +85,10 @@
     val traceEventEnd = topLevelCallableId(KtxNameConventions.TRACE_EVENT_END)
     val traceEventStart = topLevelCallableId(KtxNameConventions.TRACE_EVENT_START)
     val updateChangedFlags = topLevelCallableId(KtxNameConventions.UPDATE_CHANGED_FLAGS)
+    val rememberComposableLambda =
+        internalTopLevelCallableId(KtxNameConventions.REMEMBER_COMPOSABLE_LAMBDA)
+    val rememberComposableLambdaN =
+        internalTopLevelCallableId(KtxNameConventions.REMEMBER_COMPOSABLE_LAMBDAN)
 }
 
 object ComposeFqNames {
@@ -112,6 +116,7 @@
     val DontMemoize = fqNameFor("DontMemoize")
     val composableLambdaType = ComposeClassIds.ComposableLambda.asSingleFqName()
     val composableLambda = ComposeCallableIds.composableLambda.asSingleFqName()
+    val rememberComposableLambda = ComposeCallableIds.rememberComposableLambda.asSingleFqName()
     val composableLambdaFullName =
         internalFqNameFor("ComposableLambdaKt.composableLambda")
     val remember = ComposeCallableIds.remember.asSingleFqName()
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 9ffd47d..1059872 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
@@ -55,6 +55,7 @@
     private val sourceInformationEnabled: Boolean = true,
     private val traceMarkersEnabled: Boolean = true,
     private val intrinsicRememberEnabled: Boolean = false,
+    private val nonSkippingGroupOptimizationEnabled: Boolean = false,
     private val decoysEnabled: Boolean = false,
     private val metricsDestination: String? = null,
     private val reportsDestination: String? = null,
@@ -135,7 +136,8 @@
             metrics,
             stabilityInferencer,
             strongSkippingEnabled,
-            intrinsicRememberEnabled
+            intrinsicRememberEnabled,
+            nonSkippingGroupOptimizationEnabled,
         ).lower(moduleFragment)
 
         if (!useK2) {
@@ -206,6 +208,7 @@
             sourceInformationEnabled,
             traceMarkersEnabled,
             intrinsicRememberEnabled,
+            nonSkippingGroupOptimizationEnabled,
             strongSkippingEnabled
         ).lower(moduleFragment)
 
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
index 9911b1b..78ed369 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
@@ -64,6 +64,10 @@
         CompilerConfigurationKey<String>("Directory to save compose build reports")
     val INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY =
         CompilerConfigurationKey<Boolean>("Enable optimization to treat remember as an intrinsic")
+    val NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY =
+        CompilerConfigurationKey<Boolean>(
+            "Enabled optimization to remove groups around non-skipping functions"
+        )
     val SUPPRESS_KOTLIN_VERSION_COMPATIBILITY_CHECK = CompilerConfigurationKey<String?>(
         "Version of Kotlin for which version compatibility check should be suppressed"
     )
@@ -133,6 +137,13 @@
             required = false,
             allowMultipleOccurrences = false
         )
+        val NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_OPTION = CliOption(
+            optionName = "nonSkippingGroupOptimization",
+            valueDescription = "<true|false>",
+            description = "Remove groups around non-skipping composable functions",
+            required = false,
+            allowMultipleOccurrences = false
+        )
         val SUPPRESS_KOTLIN_VERSION_CHECK_ENABLED_OPTION = CliOption(
             "suppressKotlinVersionCompatibilityCheck",
             "<true|false>",
@@ -179,6 +190,7 @@
         METRICS_DESTINATION_OPTION,
         REPORTS_DESTINATION_OPTION,
         INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_OPTION,
+        NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_OPTION,
         SUPPRESS_KOTLIN_VERSION_CHECK_ENABLED_OPTION,
         DECOYS_ENABLED_OPTION,
         STRONG_SKIPPING_OPTION,
@@ -219,6 +231,10 @@
             ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY,
             value == "true"
         )
+        NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_OPTION -> configuration.put(
+            ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY,
+            value == "true"
+        )
         SUPPRESS_KOTLIN_VERSION_CHECK_ENABLED_OPTION -> configuration.put(
             ComposeConfiguration.SUPPRESS_KOTLIN_VERSION_COMPATIBILITY_CHECK,
             value
@@ -388,6 +404,10 @@
                 ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY,
                 true
             )
+            val nonSkippingGroupOptimizationEnabled = configuration.get(
+                ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY,
+                false
+            )
             val decoysEnabled = configuration.getBoolean(
                 ComposeConfiguration.DECOYS_ENABLED_KEY,
             )
@@ -436,6 +456,7 @@
                 sourceInformationEnabled = sourceInformationEnabled,
                 traceMarkersEnabled = traceMarkersEnabled,
                 intrinsicRememberEnabled = intrinsicRememberEnabled,
+                nonSkippingGroupOptimizationEnabled = nonSkippingGroupOptimizationEnabled,
                 decoysEnabled = decoysEnabled,
                 metricsDestination = metricsDestination,
                 reportsDestination = reportsDestination,
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/KtxNameConventions.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/KtxNameConventions.kt
index 9816b2a..2985efb 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/KtxNameConventions.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/KtxNameConventions.kt
@@ -23,4 +23,6 @@
     val UPDATE_CHANGED_FLAGS = "updateChangedFlags"
     val CURRENTMARKER = Name.identifier("currentMarker")
     val ENDTOMARKER = Name.identifier("endToMarker")
+    val REMEMBER_COMPOSABLE_LAMBDA = "rememberComposableLambda"
+    val REMEMBER_COMPOSABLE_LAMBDAN = "rememberComposableLambdaN"
 }
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 cfcf5d1..74d139b 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
@@ -453,7 +453,9 @@
         )
     }
 
-    protected fun irOr(lhs: IrExpression, rhs: IrExpression): IrCallImpl {
+    protected fun irOr(lhs: IrExpression, rhs: IrExpression): IrExpression {
+        if (rhs is IrConst<*> && rhs.value == 0) return lhs
+        if (lhs is IrConst<*> && lhs.value == 0) return rhs
         val int = context.irBuiltIns.intType
         return irCall(
             int.binaryOperator(OperatorNameConventions.OR, int),
@@ -1041,7 +1043,10 @@
                     ) {
                         return true
                     }
-                } else if (fqName == ComposeFqNames.composableLambda) {
+                } else if (
+                    fqName == ComposeFqNames.composableLambda ||
+                    fqName == ComposeFqNames.rememberComposableLambda
+                ) {
                     // calls to this function are generated by the compiler, and this
                     // function behaves similar to a remember call in that the result will
                     // _always_ be the same and the resulting type is _always_ stable, so
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 c6d875f..a9bc10d 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
@@ -127,6 +127,7 @@
 import org.jetbrains.kotlin.ir.util.getPropertyGetter
 import org.jetbrains.kotlin.ir.util.hasAnnotation
 import org.jetbrains.kotlin.ir.util.isLocal
+import org.jetbrains.kotlin.ir.util.isOverridableOrOverrides
 import org.jetbrains.kotlin.ir.util.isVararg
 import org.jetbrains.kotlin.ir.util.kotlinFqName
 import org.jetbrains.kotlin.ir.util.parentClassOrNull
@@ -457,6 +458,7 @@
     private val collectSourceInformation: Boolean,
     private val traceMarkersEnabled: Boolean,
     private val intrinsicRememberEnabled: Boolean,
+    private val nonSkippingGroupOptimizationEnabled: Boolean,
     private val strongSkippingEnabled: Boolean
 ) :
     AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer),
@@ -601,6 +603,17 @@
         getTopLevelFunction(ComposeCallableIds.sourceInformationMarkerEnd).owner
     }
 
+    private val rememberComposableLambdaFunction by guardedLazy {
+        getTopLevelFunctions(ComposeCallableIds.rememberComposableLambda).singleOrNull()
+    }
+
+    private val useNonSkippingGroupOptimization by guardedLazy {
+        // Uses `rememberComposableLambda` as a indication that the runtime supports
+        // generating remember after call as it was added at the same time as the slot table was
+        // modified to support remember after call.
+        nonSkippingGroupOptimizationEnabled && rememberComposableLambdaFunction != null
+    }
+
     private val IrType.arguments: List<IrTypeArgument>
         get() = (this as? IrSimpleType)?.arguments.orEmpty()
 
@@ -773,11 +786,18 @@
     private val IrFunction.hasNonSkippableAnnotation: Boolean
         get() = hasAnnotation(ComposeFqNames.NonSkippableComposable)
 
-    // At a high level, a non-restartable composable function
+    // At a high level, without useNonSkippingGroupOptimization, a non-restartable composable
+    // function
     // 1. gets a replaceable group placed around the body
     // 2. never calls `$composer.changed(...)` with its parameters
     // 3. can have default parameters, so needs to add the defaults preamble if defaults present
     // 4. proper groups around control flow structures in the body
+    // If supported by the runtime and useNonSkippingGroupOptimization is enabled then the
+    // replaceable group is not necessary so the above list is changed to,
+    // 1. never calls `$composer.changed(...)` with its parameters
+    // 2. can have default parameters, so needs to add the defaults preamble if defaults present
+    // 3. never elides groups around control flow structures in the body
+    // If the function has `ExplicitGroupsComposable` annotation, groups or markers should be added.
     private fun visitNonRestartableComposableFunction(
         declaration: IrFunction,
         scope: Scope.FunctionScope,
@@ -787,14 +807,21 @@
         val body = declaration.body!!
 
         val hasExplicitGroups = declaration.hasExplicitGroups
-        val elideGroups = hasExplicitGroups ||
-            declaration.hasReadOnlyAnnotation ||
+        val isReadOnly = declaration.hasReadOnlyAnnotation ||
             declaration.isComposableDelegatedAccessor()
 
+        // An outer group is required if we are a lambda or dynamic method or the runtime doesn't
+        // support remember after call. A outer group is explicitly elided by readonly and has
+        // explicit groups.
+        var outerGroupRequired = (!isReadOnly && !hasExplicitGroups &&
+            !useNonSkippingGroupOptimization) || declaration.isLambda() ||
+            declaration.isOverridableOrOverrides
+
         val skipPreamble = mutableStatementContainer()
         val bodyPreamble = mutableStatementContainer()
 
         scope.dirty = changedParam
+        scope.outerGroupRequired = outerGroupRequired
 
         val defaultScope = transformDefaults(scope)
 
@@ -806,6 +833,11 @@
             transformChildrenVoid()
         }
 
+        // If we get an early return from this function then the function itself acts like
+        // an if statement and the outer group is required if the functions is not readonly or has
+        // explicit groups.
+        if (!isReadOnly && !hasExplicitGroups && scope.hasAnyEarlyReturn) outerGroupRequired = true
+
         buildPreambleStatementsAndReturnIfSkippingPossible(
             body,
             skipPreamble,
@@ -824,13 +856,16 @@
             transformed.wrapWithTraceEvents(irFunctionSourceKey(), scope)
         }
 
-        if (!elideGroups) {
+        if (outerGroupRequired) {
             scope.realizeGroup {
                 irComposite(statements = listOfNotNull(
                     if (emitTraceMarkers) irTraceEventEnd() else null,
                     irEndReplaceableGroup(scope = scope)
                 ))
             }
+        } else if (useNonSkippingGroupOptimization) {
+            scope.realizeAllDirectChildren()
+            scope.realizeCoalescableGroup()
         }
 
         declaration.body = IrBlockBodyImpl(
@@ -838,7 +873,7 @@
             body.endOffset,
             listOfNotNull(
                 when {
-                    !elideGroups ->
+                    outerGroupRequired ->
                         irStartReplaceableGroup(
                             body,
                             scope,
@@ -856,7 +891,7 @@
                 *bodyPreamble.statements.toTypedArray(),
                 *transformed.statements.toTypedArray(),
                 when {
-                    !elideGroups -> irEndReplaceableGroup(scope = scope)
+                    outerGroupRequired -> irEndReplaceableGroup(scope = scope)
                     collectSourceInformation && !hasExplicitGroups ->
                         irSourceInformationMarkerEnd(body, scope)
                     else -> null
@@ -864,7 +899,7 @@
                 returnVar?.let { irReturnVar(declaration.symbol, it) }
             )
         )
-        if (elideGroups && !hasExplicitGroups) {
+        if (!outerGroupRequired && !hasExplicitGroups) {
             scope.realizeEndCalls {
                 irComposite(
                     statements = listOfNotNull(
@@ -884,7 +919,7 @@
             isLambda = declaration.isLambda(),
             inline = declaration.isInline,
             hasDefaults = false,
-            readonly = elideGroups,
+            readonly = isReadOnly,
         )
 
         scope.metrics.recordGroup()
@@ -987,7 +1022,7 @@
             // replace dirty with changed param in meta used for inference, as we are not
             // populating dirty
             if (!canSkipExecution) {
-                metas.forEach {
+                metas.fastForEach {
                     if (it.paramRef?.maskParam == dirty) {
                         it.paramRef?.maskParam = changedParam
                     }
@@ -1153,7 +1188,7 @@
             // replace dirty with changed param in meta used for inference, as we are not
             // populating dirty
             if (!canSkipExecution) {
-                metas.forEach {
+                metas.fastForEach {
                     if (it.paramRef?.maskParam == dirty) {
                         it.paramRef?.maskParam = changedParam
                     }
@@ -1277,7 +1312,7 @@
     private fun transformDefaults(scope: Scope.FunctionScope): Scope.ParametersScope {
         val parameters = scope.allTrackedParams
         val parametersScope = Scope.ParametersScope()
-        parameters.forEach { param ->
+        parameters.fastForEach { param ->
             val defaultValue = param.defaultValue
             if (defaultValue != null) {
                 defaultValue.expression = inScope(parametersScope) {
@@ -1336,7 +1371,7 @@
         val setDefaults = mutableStatementContainer()
         val skipDefaults = mutableStatementContainer()
 //        val parametersScope = Scope.ParametersScope()
-        parameters.forEachIndexed { slotIndex, param ->
+        parameters.fastForEachIndexed { slotIndex, param ->
             val defaultIndex = scope.defaultIndexForSlotIndex(slotIndex)
             val defaultValue = param.defaultValue?.expression
             if (defaultParam != null && defaultValue != null) {
@@ -1388,7 +1423,7 @@
             }
         }
 
-        parameters.forEachIndexed { slotIndex, param ->
+        parameters.fastForEachIndexed { slotIndex, param ->
             val stability = stabilityInferencer.stabilityOf(param.varargElementType ?: param.type)
 
             stabilities[slotIndex] = stability
@@ -1417,9 +1452,9 @@
         // of the function's group. Note that these end up getting called *before* default
         // expressions, but this is okay because it will only ever get called on parameters that
         // are provided to the function
-        parameters.forEachIndexed { slotIndex, param ->
+        parameters.fastForEachIndexed { slotIndex, param ->
             // varargs get handled separately because they will require their own groups
-            if (param.isVararg) return@forEachIndexed
+            if (param.isVararg) return@fastForEachIndexed
             val defaultIndex = scope.defaultIndexForSlotIndex(slotIndex)
             val defaultValue = param.defaultValue
             val stability = stabilities[slotIndex]
@@ -1508,8 +1543,8 @@
         }
 
         // now we handle the vararg parameters specially since it needs to create a group
-        parameters.forEachIndexed { slotIndex, param ->
-            val varargElementType = param.varargElementType ?: return@forEachIndexed
+        parameters.fastForEachIndexed { slotIndex, param ->
+            val varargElementType = param.varargElementType ?: return@fastForEachIndexed
             if (mightSkip && dirty is IrChangedBitMaskVariable) {
                 // for vararg parameters of stable type, we can store each value in the slot
                 // table, but need to generate a group since the size of the array could change
@@ -1579,7 +1614,7 @@
                 )
             }
         }
-        parameters.forEach {
+        parameters.fastForEach {
             // we want to remove the default expression from the function. This will prevent
             // the kotlin compiler from doing its own default handling, which we don't need.
             it.defaultValue = null
@@ -1721,7 +1756,7 @@
                     irCall(function.symbol).apply {
                         symbol.owner
                             .valueParameters
-                            .forEachIndexed { index, param ->
+                            .fastForEachIndexed { index, param ->
                                 if (param.isVararg) {
                                     putValueArgument(
                                         index,
@@ -1764,7 +1799,7 @@
 
                         extensionReceiver = function.extensionReceiverParameter?.let { irGet(it) }
                         dispatchReceiver = outerReceiver?.let { irGet(it) }
-                        function.typeParameters.forEachIndexed { index, parameter ->
+                        function.typeParameters.fastForEachIndexed { index, parameter ->
                             putTypeArgument(index, parameter.defaultType)
                         }
                     }
@@ -1946,6 +1981,12 @@
             .kotlinFqName
             .asString()
             .hashCode()
+        // Use the start offset of the first statement if it is a block (and has a first statement)
+        // This eliminates the ambiguity of a while loop and its block which has the same start
+        // and end offsets.
+        val startOffset = if (this is IrBlock)
+            statements.firstOrNull()?.startOffset ?: startOffset
+        else startOffset
         hash = 31 * hash + startOffset
         if (this is IrConst<*>) {
             // Disambiguate ?. clauses which become a "null" constant expression
@@ -2032,6 +2073,32 @@
         }
     }
 
+    private fun irSourceInformationMarkerEnd(
+        element: IrElement,
+        scope: Scope.BlockScope
+    ): IrExpression {
+        return irCall(
+            sourceInformationMarkerEndFunction,
+            element.startOffset,
+            element.endOffset
+        ).also {
+            it.putValueArgument(0, scope.irCurrentComposer())
+        }
+    }
+
+    private fun irWithSourceInformationMarker(
+        expression: IrExpression,
+        scope: Scope.BlockScope,
+        before: List<IrStatement>
+    ): IrExpression = if (collectSourceInformation && scope.hasSourceInformation) {
+        expression.wrap(
+            before = before + listOf(irSourceInformationMarkerStart(expression, scope)),
+            after = listOf(irSourceInformationMarkerEnd(expression, scope))
+        )
+    } else if (before.isNotEmpty())
+        expression.wrap(before = before)
+    else expression
+
     private fun irIsTraceInProgress(): IrExpression? =
         isTraceInProgressFunction?.let { irCall(it) }
 
@@ -2077,19 +2144,6 @@
             irIfTraceInProgress(irCall(it))
         }
 
-    private fun irSourceInformationMarkerEnd(
-        element: IrElement,
-        scope: Scope.BlockScope
-    ): IrExpression {
-        return irCall(
-            sourceInformationMarkerEndFunction,
-            element.startOffset,
-            element.endOffset
-        ).also {
-            it.putValueArgument(0, scope.irCurrentComposer())
-        }
-    }
-
     private fun irStartDefaults(element: IrElement): IrExpression {
         return irMethodCall(
             irCurrentComposer(),
@@ -2214,7 +2268,7 @@
                     thenPart = irNull(),
                     elsePart = irCall(symbol).apply {
                         dispatchReceiver = irGet(tmpVal)
-                        args.forEachIndexed { i, arg ->
+                        args.fastForEachIndexed { i, arg ->
                             putValueArgument(i, arg)
                         }
                     }
@@ -2390,7 +2444,7 @@
     private fun IrContainerExpression.asSourceOrEarlyExitGroup(
         scope: Scope.FunctionScope
     ): IrContainerExpression {
-        if (scope.hasEarlyReturn) {
+        if (scope.hasInlineEarlyReturn) {
             currentFunctionScope.metrics.recordGroup()
         } else if (!collectSourceInformation) {
             // If we are not generating source information and the lambda does not contain an
@@ -2402,7 +2456,7 @@
         // the group, and we don't have to deal with any of the complicated jump logic that
         // could be inside of the block
         val makeStart = {
-            if (scope.hasEarlyReturn) irStartReplaceableGroup(
+            if (scope.hasInlineEarlyReturn) irStartReplaceableGroup(
                 this,
                 scope,
                 startOffset = startOffset,
@@ -2411,7 +2465,7 @@
             else irSourceInformationMarkerStart(this, scope)
         }
         val makeEnd = {
-            if (scope.hasEarlyReturn) irEndReplaceableGroup(scope = scope)
+            if (scope.hasInlineEarlyReturn) irEndReplaceableGroup(scope = scope)
             else irSourceInformationMarkerEnd(this, scope)
         }
         if (!scope.hasComposableCalls && !scope.hasReturn && !scope.hasJump) {
@@ -2546,13 +2600,14 @@
             when (scope) {
                 is Scope.FunctionScope -> {
                     if (scope.function == symbol.owner) {
+                        scope.hasAnyEarlyReturn = true
                         if (!leavingInlinedLambda || !rollbackGroupMarkerEnabled) {
-                            blockScopeMarks.forEach {
+                            blockScopeMarks.fastForEach {
                                 it.markReturn(extraEndLocation)
                             }
                             scope.markReturn(extraEndLocation)
                             if (scope.isInlinedLambda && scope.inComposableCall) {
-                                scope.hasEarlyReturn = true
+                                scope.hasInlineEarlyReturn = true
                             }
                         } else {
                             val functionScope = scope
@@ -2560,7 +2615,7 @@
                             if (functionScope.isInlinedLambda) {
                                 val marker = irGet(functionScope.allocateMarker())
                                 extraEndLocation(irEndToMarker(marker, targetScope))
-                                scope.hasEarlyReturn = true
+                                scope.hasInlineEarlyReturn = true
                             } else {
                                 val marker = functionScope.allocateMarker()
                                 functionScope.markReturn {
@@ -2573,7 +2628,7 @@
                     }
                     if (scope.isInlinedLambda && scope.inComposableCall) {
                         leavingInlinedLambda = true
-                        scope.hasEarlyReturn = true
+                        scope.hasInlineEarlyReturn = true
                     }
                 }
                 is Scope.BlockScope -> {
@@ -3009,7 +3064,7 @@
             dispatchArg = dispatchMeta
         )
 
-        changedParams.forEachIndexed { i, param ->
+        changedParams.fastForEachIndexed { i, param ->
             expression.putValueArgument(changedArgIndex + i, param)
         }
 
@@ -3094,7 +3149,7 @@
         // If intrinsic remember uses $dirty, we are not sure if it is going to be populated,
         // so we have to apply fixups after function body is transformed
         var dirty: IrChangedBitMaskValue? = null
-        inputArgMetas.forEach {
+        inputArgMetas.fastForEach {
             val meta = it.paramRef
             if (meta?.maskParam is IrChangedBitMaskVariable) {
                 if (dirty == null) {
@@ -3172,12 +3227,20 @@
 
         val blockScope = intrinsicRememberScope(expression)
         return inScope(blockScope) {
-            cacheCall.wrap(
-                before = inputVals.filterNotNull() + listOf(
-                    irStartReplaceableGroup(expression, blockScope)
-                ),
-                after = listOf(irEndReplaceableGroup(scope = blockScope))
-            )
+            val nonNullInputValues = inputVals.filterNotNull()
+            if (useNonSkippingGroupOptimization)
+                irWithSourceInformationMarker(
+                    before = nonNullInputValues,
+                    expression = cacheCall,
+                    scope = blockScope,
+                )
+            else
+                cacheCall.wrap(
+                    before = inputVals.filterNotNull() + listOf(
+                        irStartReplaceableGroup(expression, blockScope)
+                    ),
+                    after = listOf(irEndReplaceableGroup(scope = blockScope))
+                )
         }.also { block ->
             if (
                 stabilityInferencer.stabilityOf(block.type).knownStable() &&
@@ -3448,7 +3511,7 @@
         var bitMaskConstant = 0b0
         val orExprs = mutableListOf<IrExpression>()
 
-        arguments.forEachIndexed { slot, argInfo ->
+        arguments.fastForEachIndexed { slot, argInfo ->
             val stability = argInfo.stability
             when {
                 !strongSkippingEnabled && stability.knownUnstable() -> {
@@ -3456,7 +3519,7 @@
                     // If it is known to be unstable, there's no purpose in propagating any
                     // additional metadata _for this parameter_, but we still want to propagate
                     // the other parameters.
-                    return@forEachIndexed
+                    return@fastForEachIndexed
                 }
                 stability.knownStable() -> {
                     bitMaskConstant = bitMaskConstant or StabilityBits.STABLE.bitsForSlot(slot)
@@ -3671,7 +3734,13 @@
                 }
             }
         }
-        return if (!loopScope.needsGroupPerIteration && loopScope.hasComposableCalls) {
+        return if ((!loopScope.needsGroupPerIteration || (
+                !currentFunctionScope.outerGroupRequired &&
+                // if we end up getting an early return this group will come back
+                // However this might generate less efficient (but still correct code) if the
+                // early return is encountered after the loop.
+                !currentFunctionScope.hasAnyEarlyReturn)
+            ) && loopScope.hasComposableCalls) {
             // If a loop contains composable calls but not a otherwise need a group per iteration
             // group, none of the children can be coalesced and must be realized as the second
             // iteration as composable calls at the end might end of overlapping slots with the
@@ -3709,7 +3778,7 @@
         val resultScopes = mutableListOf<Scope.BranchScope>()
         val condScopes = mutableListOf<Scope.BranchScope>()
         val whenScope = withScope(Scope.WhenScope()) {
-            expression.branches.forEachIndexed { index, it ->
+            expression.branches.fastForEachIndexed { index, it ->
                 if (it is IrElseBranch) {
                     hasElseBranch = true
                     val (resultScope, result) = it.result.transformWithScope(Scope.BranchScope())
@@ -3857,7 +3926,8 @@
 
             val metrics: FunctionMetrics = transformer.metricsFor(function)
 
-            var hasEarlyReturn: Boolean = false
+            var hasInlineEarlyReturn: Boolean = false
+            var hasAnyEarlyReturn: Boolean = false
 
             private var lastTemporaryIndex: Int = 0
 
@@ -3890,6 +3960,8 @@
 
             var dirty: IrChangedBitMaskValue? = null
 
+            var outerGroupRequired = false
+
             val markerPreamble = mutableStatementContainer(transformer.context)
             private var marker: IrVariable? = null
 
@@ -4079,7 +4151,7 @@
                     List<CallArgumentMeta>
                 ) -> IrExpression
             ) {
-                intrinsicRememberFixups.forEach {
+                intrinsicRememberFixups.fastForEach {
                     val invalid = invalidExpr(it.isMemoizedLambda, it.args, it.metas)
                     // $composer.cache(invalid, calc)
                     it.call.putValueArgument(0, invalid)
@@ -4112,7 +4184,7 @@
 
             fun realizeAllDirectChildren() {
                 if (coalescableChilds.isNotEmpty()) {
-                    coalescableChilds.forEach {
+                    coalescableChilds.fastForEach {
                         it.shouldRealize = true
                     }
                 }
@@ -4186,13 +4258,13 @@
             }
 
             fun realizeCoalescableGroup() {
-                coalescableChilds.forEach {
+                coalescableChilds.fastForEach {
                     it.realize()
                 }
             }
 
             open fun realizeEndCalls(makeEnd: () -> IrExpression) {
-                extraEndLocations.forEach {
+                extraEndLocations.fastForEach {
                     it(makeEnd())
                 }
             }
@@ -4265,9 +4337,10 @@
             override fun realizeEndCalls(makeEnd: () -> IrExpression) {
                 super.realizeEndCalls(makeEnd)
                 if (needsGroupPerIteration) {
-                    jumpEndLocations.forEach {
+                    jumpEndLocations.fastForEach {
                         it(makeEnd())
                     }
+                    jumpEndLocations.clear()
                 }
             }
         }
@@ -4382,7 +4455,7 @@
         }
 
         override fun putAsValueArgumentIn(fn: IrFunctionAccessExpression, startIndex: Int) {
-            params.forEachIndexed { i, param ->
+            params.fastForEachIndexed { i, param ->
                 fn.putValueArgument(
                     startIndex + i,
                     irGet(param)
@@ -4559,7 +4632,7 @@
             lowBit: Boolean
         ) {
             used = true
-            params.forEachIndexed { index, param ->
+            params.fastForEachIndexed { index, param ->
                 fn.putValueArgument(
                     startIndex + index,
                     if (index == 0) {
@@ -4655,6 +4728,27 @@
     }
 }
 
+inline fun <T> List<T>.fastForEach(action: (T) -> Unit) {
+    for (i in indices) {
+        val item = get(i)
+        action(item)
+    }
+}
+
+inline fun <T> List<T>.fastForEachIndexed(action: (index: Int, T) -> Unit) {
+    for (i in indices) {
+        val item = get(i)
+        action(i, item)
+    }
+}
+
+inline fun <T> Array<out T>.fastForEachIndexed(action: (index: Int, T) -> Unit) {
+    for (i in indices) {
+        val item = get(i)
+        action(i, item)
+    }
+}
+
 private fun IrType.isClassType(fqName: FqNameUnsafe, hasQuestionMark: Boolean? = null): Boolean {
     if (this !is IrSimpleType) return false
     if (hasQuestionMark != null && this.isMarkedNullable() == hasQuestionMark) return false
@@ -4745,7 +4839,7 @@
         }
     }
 
-    parameters.forEachIndexed { originalIndex, parameter ->
+    parameters.fastForEachIndexed { originalIndex, parameter ->
         if (expectedIndexes.first() == sortIndex[originalIndex] &&
             !parameter.type.isInlineClassType()
         ) {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
index 09c55cde..1ecc29f 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
@@ -66,7 +66,6 @@
 import org.jetbrains.kotlin.ir.expressions.IrCall
 import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
 import org.jetbrains.kotlin.ir.expressions.IrExpression
-import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
 import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
 import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
 import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
@@ -267,8 +266,8 @@
     override fun declareLocal(local: IrValueDeclaration?) {}
     override fun recordCapture(local: IrValueDeclaration?): Boolean {
         val isThis = local == thisParam
-        val isCtorParam = (local?.parent as? IrConstructor)?.parent === declaration
-        val isClassParam = isThis || isCtorParam
+        val isConstructorParam = (local?.parent as? IrConstructor)?.parent === declaration
+        val isClassParam = isThis || isConstructorParam
         if (local != null && collectors.isNotEmpty() && isClassParam) {
             for (collector in collectors) {
                 collector.recordCapture(local)
@@ -297,7 +296,8 @@
     metrics: ModuleMetrics,
     stabilityInferencer: StabilityInferencer,
     private val strongSkippingModeEnabled: Boolean,
-    private val intrinsicRememberEnabled: Boolean
+    private val intrinsicRememberEnabled: Boolean,
+    private val nonSkippingGroupOptimizationEnabled: Boolean,
 ) : AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer),
 
     ModuleLoweringPass {
@@ -316,6 +316,37 @@
     private val rememberFunctions =
         getTopLevelFunctions(ComposeCallableIds.remember).map { it.owner }
 
+    private val composableLambdaFunction by guardedLazy {
+        getTopLevelFunction(ComposeCallableIds.composableLambda)
+    }
+
+    private val composableLambdaNFunction by guardedLazy {
+        getTopLevelFunction(ComposeCallableIds.composableLambdaN)
+    }
+
+    private val composableLambdaInstanceFunction by guardedLazy {
+        getTopLevelFunction(ComposeCallableIds.composableLambdaInstance)
+    }
+
+    private val composableLambdaInstanceNFunction by guardedLazy {
+        getTopLevelFunction(ComposeCallableIds.composableLambdaNInstance)
+    }
+
+    private val rememberComposableLambdaFunction by guardedLazy {
+        getTopLevelFunctions(ComposeCallableIds.rememberComposableLambda).singleOrNull()
+    }
+
+    private val rememberComposableLambdaNFunction by guardedLazy {
+        getTopLevelFunctions(ComposeCallableIds.rememberComposableLambdaN).singleOrNull()
+    }
+
+    private val useNonSkippingGroupOptimization by guardedLazy {
+        // Uses `rememberComposableLambda` as a indication that the runtime supports
+        // generating remember after call as it was added at the same time as the slot table was
+        // modified to support remember after call.
+        nonSkippingGroupOptimizationEnabled && rememberComposableLambdaFunction != null
+    }
+
     private fun getOrCreateComposableSingletonsClass(): IrClass {
         if (composableSingletonsClass != null) return composableSingletonsClass!!
         val declaration = currentFile!!
@@ -338,8 +369,8 @@
             // inside of this class with actual PSI in the editor.
             it.addConstructor {
                 isPrimary = true
-            }.also { ctor ->
-                ctor.body = DeclarationIrBuilder(context, it.symbol).irBlockBody {
+            }.also { constructor ->
+                constructor.body = DeclarationIrBuilder(context, it.symbol).irBlockBody {
                     +irDelegatingConstructorCall(
                         context
                             .irBuiltIns
@@ -847,16 +878,18 @@
 
         val useComposableLambdaN = argumentCount > MAX_RESTART_ARGUMENT_COUNT
         val useComposableFactory = collector.hasCaptures && declarationContext.composable
-        val restartFunctionFactory =
+        val rememberComposableN = rememberComposableLambdaNFunction ?: composableLambdaNFunction
+        val rememberComposable = rememberComposableLambdaFunction ?: composableLambdaFunction
+        val requiresExplicitComposerParameter = useComposableFactory &&
+            rememberComposableLambdaFunction == null
+        val restartFactorySymbol =
             if (useComposableFactory)
                 if (useComposableLambdaN)
-                    ComposeCallableIds.composableLambdaN
-                else ComposeCallableIds.composableLambda
+                    rememberComposableN
+                else rememberComposable
             else if (useComposableLambdaN)
-                ComposeCallableIds.composableLambdaNInstance
-            else ComposeCallableIds.composableLambdaInstance
-        val restartFactorySymbol =
-            getTopLevelFunction(restartFunctionFactory)
+                composableLambdaInstanceNFunction
+            else composableLambdaInstanceFunction
         val irBuilder = DeclarationIrBuilder(
             context,
             symbol = declarationContext.symbol,
@@ -872,7 +905,7 @@
             var index = 0
 
             // first parameter is the composer parameter if we are using the composable factory
-            if (useComposableFactory) {
+            if (requiresExplicitComposerParameter) {
                 putValueArgument(
                     index++,
                     irCurrentComposer()
@@ -992,18 +1025,23 @@
             calculation
         )
 
-        val fqName = currentFunctionContext?.declaration?.kotlinFqName?.asString()
-        val key = fqName.hashCode() + expression.startOffset
-        // Wrap the cached expression in a replaceable group
-        val cacheTmpVar = irTemporary(cache, "tmpCache")
-        return cacheTmpVar.wrap(
-            type = expression.type,
-            before = listOf(irStartReplaceableGroup(irCurrentComposer(), irConst(key))),
-            after = listOf(
-                irEndReplaceableGroup(irCurrentComposer()),
-                irGet(cacheTmpVar)
+        return if (nonSkippingGroupOptimizationEnabled) {
+            cache
+        } else {
+            // If the non-skipping group optimization is disabled then we need to wrap
+            // the call to `cache` in a replaceable group.
+            val fqName = currentFunctionContext?.declaration?.kotlinFqName?.asString()
+            val key = fqName.hashCode() + expression.startOffset
+            val cacheTmpVar = irTemporary(cache, "tmpCache")
+            cacheTmpVar.wrap(
+                type = expression.type,
+                before = listOf(irStartReplaceableGroup(irCurrentComposer(), irConst(key))),
+                after = listOf(
+                    irEndReplaceableGroup(irCurrentComposer()),
+                    irGet(cacheTmpVar)
+                )
             )
-        )
+        }
     }
 
     private fun irRemember(
@@ -1096,19 +1134,6 @@
             (parent as? IrFunction)?.isInline == true &&
             !isNoinline
 
-    private fun <T : IrFunctionAccessExpression> T.markAsSynthetic(mark: Boolean): T {
-        if (mark) {
-            // Mark it so the ComposableCallTransformer will insert the correct code around this
-            // call
-            context.irTrace.record(
-                ComposeWritableSlices.IS_SYNTHETIC_COMPOSABLE_CALL,
-                this,
-                true
-            )
-        }
-        return this
-    }
-
     private fun <T : IrExpression> T.markAsStatic(mark: Boolean): T {
         if (mark) {
             // Mark it so the ComposableCallTransformer will insert the correct code around this
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 0b0f561..63c8129 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
@@ -893,17 +893,19 @@
                 val rhsStatement = expression.statements[1]
                 val rhs = when (rhsStatement) {
                     is IrBlock -> {
-                        val target = rhsStatement.statements[1]
-                        when (target) {
-                            is IrVariable -> target.initializer
-                            else -> target
-                        }
+                        if (rhsStatement.statements.size == 2) {
+                            val target = rhsStatement.statements[1]
+                            when (target) {
+                                is IrVariable -> target.initializer
+                                else -> target
+                            }
+                        } else rhsStatement
                     }
                     else -> {
                         rhsStatement
                     }
-                } as IrWhen
-                val call = rhs.branches.last().result as? IrCall
+                } as? IrWhen
+                val call = rhs?.let { it.branches.last().result as? IrCall }
                 if (call == null) {
                     expression.statements.printJoin("\n")
                     return
diff --git a/compose/compiler/snapshot-compose-compiler.sh b/compose/compiler/snapshot-compose-compiler.sh
index 7436318..d3393c0 100755
--- a/compose/compiler/snapshot-compose-compiler.sh
+++ b/compose/compiler/snapshot-compose-compiler.sh
@@ -16,6 +16,6 @@
 set -e
 GRADLEW_LOCATION="$(dirname $0)"/../../gradlew
 VERIFICATION_METADATA="$(dirname $0)"/../../gradle/verification-metadata.xml
-REPOSITORY_LOCATION="$(dirname $0)"/compose-compiler-snapshot-repository
+REPOSITORY_LOCATION="$(realpath $(dirname $0))"/compiler/compose-compiler-snapshot-repository
 COMPOSE_CUSTOM_VERSION=99.0.0 $GRADLEW_LOCATION -Dmaven.repo.local=$REPOSITORY_LOCATION -Pandroidx.versionExtraCheckEnabled=false :compose:compiler:compiler:publishToMavenLocal --stacktrace
 rm -f $VERIFICATION_METADATA
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt
index 912f063..16f6d92 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt
@@ -61,7 +61,7 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.ApplicationScope
-import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogWindow
 import androidx.compose.ui.window.Notification
 import androidx.compose.ui.window.Popup
 import androidx.compose.ui.window.TrayState
@@ -276,7 +276,7 @@
                 backgroundColor = Color(70, 70, 70)
             )
         } else {
-            Dialog(
+            DialogWindow(
                 onCloseRequest = dismiss
             ) {
                 WindowContent(
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/windowapi/Examples.jvm.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/windowapi/Examples.jvm.kt
index a74900c..84f45b9 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/windowapi/Examples.jvm.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/windowapi/Examples.jvm.kt
@@ -47,7 +47,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.ApplicationScope
 import androidx.compose.ui.window.AwtWindow
-import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogWindow
 import androidx.compose.ui.window.MenuBar
 import androidx.compose.ui.window.Notification
 import androidx.compose.ui.window.Tray
@@ -262,7 +262,7 @@
             }
 
             if (isDialogShowing) {
-                Dialog(onCloseRequest = { isDialogShowing = false }) {
+                DialogWindow(onCloseRequest = { isDialogShowing = false }) {
                     Text("Dialog")
                 }
             }
@@ -279,7 +279,7 @@
             Text("Dialog")
         }
 
-        Dialog(
+        DialogWindow(
             onCloseRequest = { isDialogVisible = false },
             visible = isDialogVisible
         ) {
diff --git a/compose/foundation/foundation-layout/build.gradle b/compose/foundation/foundation-layout/build.gradle
index 126368e..062412b 100644
--- a/compose/foundation/foundation-layout/build.gradle
+++ b/compose/foundation/foundation-layout/build.gradle
@@ -63,7 +63,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api("androidx.annotation:annotation:1.1.0")
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
                 implementation("androidx.core:core:1.7.0")
                 implementation("androidx.compose.animation:animation-core:1.2.1")
             }
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurementHelper.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurementHelper.kt
index 488842c..ddd28bb 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurementHelper.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurementHelper.kt
@@ -180,18 +180,29 @@
                         0,
                         (weightUnitSpace * weight).fastRoundToInt() + remainderUnit
                     )
-                    val placeable = child.measure(
-                        OrientationIndependentConstraints(
-                            if (parentData.fill && childMainAxisSize != Constraints.Infinity) {
-                                childMainAxisSize
-                            } else {
-                                0
-                            },
-                            childMainAxisSize,
-                            crossAxisMin = crossAxisDesiredSize ?: 0,
-                            crossAxisMax = crossAxisDesiredSize ?: constraints.crossAxisMax
-                        ).toBoxConstraints(orientation)
+                    val restrictedConstraints = Constraints.restrictedConstraints(
+                        minWidth = if (parentData.fill &&
+                            childMainAxisSize != Constraints.Infinity
+                        ) {
+                            childMainAxisSize
+                        } else {
+                            0
+                        },
+                        maxWidth = childMainAxisSize,
+                        minHeight = crossAxisDesiredSize ?: 0,
+                        maxHeight = crossAxisDesiredSize ?: constraints.crossAxisMax
                     )
+                    val childConstraints = if (orientation == LayoutOrientation.Horizontal) {
+                        restrictedConstraints
+                    } else {
+                        Constraints(
+                            minHeight = restrictedConstraints.minWidth,
+                            maxHeight = restrictedConstraints.maxWidth,
+                            minWidth = restrictedConstraints.minHeight,
+                            maxWidth = restrictedConstraints.maxHeight,
+                        )
+                    }
+                    val placeable = child.measure(childConstraints)
                     weightedSpace += placeable.mainAxisSize()
                     crossAxisSpace = max(crossAxisSpace, placeable.crossAxisSize())
                     anyAlignBy = anyAlignBy || parentData.isRelative
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index aac3ce7..202b7fa 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -288,6 +288,10 @@
     method public static androidx.compose.ui.Modifier systemGestureExclusion(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LayoutCoordinates,androidx.compose.ui.geometry.Rect> exclusion);
   }
 
+  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public fun interface TextLinkClickHandler {
+    method public void onClick(androidx.compose.ui.text.LinkAnnotation link);
+  }
+
 }
 
 package androidx.compose.foundation.content {
@@ -569,6 +573,11 @@
     method @Deprecated public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
   }
 
+  @androidx.compose.runtime.Stable public interface TargetedFlingBehavior extends androidx.compose.foundation.gestures.FlingBehavior {
+    method public default suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
+    method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onRemainingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
+  }
+
   public final class TransformGestureDetectorKt {
     method public static long calculateCentroid(androidx.compose.ui.input.pointer.PointerEvent, optional boolean useCurrent);
     method public static float calculateCentroidSize(androidx.compose.ui.input.pointer.PointerEvent, optional boolean useCurrent);
@@ -618,15 +627,14 @@
     method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState);
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.FlingBehavior {
+  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.TargetedFlingBehavior {
     ctor @Deprecated public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.AnimationSpec<java.lang.Float> lowVelocityAnimationSpec, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> highVelocityAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
     ctor public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
-    method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
-    method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onSettlingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
+    method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onRemainingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
   }
 
   public final class SnapFlingBehaviorKt {
-    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.snapping.SnapFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider);
+    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.TargetedFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider);
   }
 
   @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public interface SnapLayoutInfoProvider {
@@ -1492,6 +1500,7 @@
   public final class BasicTextKt {
     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,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 @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, optional androidx.compose.ui.graphics.ColorProducer? color);
+    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @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, optional androidx.compose.ui.graphics.ColorProducer? color, optional androidx.compose.foundation.TextLinkClickHandler? onLinkClicked);
     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,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, 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,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
     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,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 526e75f..719edf3 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -290,6 +290,10 @@
     method public static androidx.compose.ui.Modifier systemGestureExclusion(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LayoutCoordinates,androidx.compose.ui.geometry.Rect> exclusion);
   }
 
+  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public fun interface TextLinkClickHandler {
+    method public void onClick(androidx.compose.ui.text.LinkAnnotation link);
+  }
+
 }
 
 package androidx.compose.foundation.content {
@@ -571,6 +575,11 @@
     method @Deprecated public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
   }
 
+  @androidx.compose.runtime.Stable public interface TargetedFlingBehavior extends androidx.compose.foundation.gestures.FlingBehavior {
+    method public default suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
+    method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onRemainingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
+  }
+
   public final class TransformGestureDetectorKt {
     method public static long calculateCentroid(androidx.compose.ui.input.pointer.PointerEvent, optional boolean useCurrent);
     method public static float calculateCentroidSize(androidx.compose.ui.input.pointer.PointerEvent, optional boolean useCurrent);
@@ -620,15 +629,14 @@
     method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState);
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.FlingBehavior {
+  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.TargetedFlingBehavior {
     ctor @Deprecated public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.AnimationSpec<java.lang.Float> lowVelocityAnimationSpec, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> highVelocityAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
     ctor public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
-    method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
-    method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onSettlingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
+    method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onRemainingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
   }
 
   public final class SnapFlingBehaviorKt {
-    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.snapping.SnapFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider);
+    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.TargetedFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider);
   }
 
   @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public interface SnapLayoutInfoProvider {
@@ -1494,6 +1502,7 @@
   public final class BasicTextKt {
     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,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 @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, optional androidx.compose.ui.graphics.ColorProducer? color);
+    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @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, optional androidx.compose.ui.graphics.ColorProducer? color, optional androidx.compose.foundation.TextLinkClickHandler? onLinkClicked);
     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,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, 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,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
     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,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/pager/PagerScrollingBenchmark.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/pager/PagerScrollingBenchmark.kt
index aa4a1aae..5930853 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/pager/PagerScrollingBenchmark.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/pager/PagerScrollingBenchmark.kt
@@ -23,6 +23,7 @@
 import androidx.compose.foundation.benchmark.lazy.toggleStateBenchmark
 import androidx.compose.foundation.benchmark.lazy.toggleStateBenchmarkDraw
 import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
 import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
 import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
 import androidx.compose.foundation.layout.Box
@@ -302,6 +303,9 @@
     beyondBoundsPageCount: Int
 ) -> Unit =
     { state, useKeys, beyondBoundsPageCount ->
+        val flingBehavior = rememberSnapFlingBehavior(
+            snapLayoutInfoProvider = NoOpInfoProvider
+        ) as SnapFlingBehavior
         VerticalPager(
             state = state, modifier = Modifier
                 .requiredHeight(400.dp)
@@ -313,7 +317,7 @@
             },
             pageSize = PageSize.Fixed(30.dp),
             outOfBoundsPageCount = beyondBoundsPageCount,
-            flingBehavior = rememberSnapFlingBehavior(snapLayoutInfoProvider = NoOpInfoProvider)
+            flingBehavior = flingBehavior
         ) {
             Box(Modifier.fillMaxSize())
         }
@@ -326,6 +330,9 @@
     beyondBoundsPageCount: Int
 ) -> Unit =
     { state, useKeys, beyondBoundsPageCount ->
+        val flingBehavior = rememberSnapFlingBehavior(
+            snapLayoutInfoProvider = NoOpInfoProvider
+        ) as SnapFlingBehavior
         HorizontalPager(
             state = state, modifier = Modifier
                 .requiredWidth(400.dp)
@@ -337,7 +344,7 @@
             },
             pageSize = PageSize.Fixed(30.dp),
             outOfBoundsPageCount = beyondBoundsPageCount,
-            flingBehavior = rememberSnapFlingBehavior(snapLayoutInfoProvider = NoOpInfoProvider)
+            flingBehavior = flingBehavior
         ) {
             Box(Modifier.fillMaxSize())
         }
diff --git a/compose/foundation/foundation/build.gradle b/compose/foundation/foundation/build.gradle
index 5822984..8afc1b1 100644
--- a/compose/foundation/foundation/build.gradle
+++ b/compose/foundation/foundation/build.gradle
@@ -42,7 +42,7 @@
         commonMain {
             dependencies {
                 implementation(libs.kotlinStdlibCommon)
-                api(project(':collection:collection'))
+                api("androidx.collection:collection:1.4.0")
                 api(project(':compose:animation:animation'))
                 api(project(':compose:runtime:runtime'))
                 api(project(':compose:ui:ui'))
@@ -69,7 +69,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api("androidx.annotation:annotation:1.1.0")
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
                 implementation("androidx.emoji2:emoji2:1.3.0")
                 implementation("androidx.core:core:1.12.0")
             }
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/build.gradle b/compose/foundation/foundation/integration-tests/foundation-demos/build.gradle
index 5522a97..ff44780 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/build.gradle
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/build.gradle
@@ -41,7 +41,7 @@
     implementation(project(":compose:ui:ui-tooling-preview"))
     debugImplementation(project(":compose:ui:ui-tooling"))
     implementation(project(":internal-testutils-fonts"))
-    implementation(project(":collection:collection"))
+    implementation("androidx.collection:collection:1.4.0")
 }
 
 android {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/Hyperlinks.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/Hyperlinks.kt
index c73c048..f34f9a3 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/Hyperlinks.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/Hyperlinks.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.demos.text
 
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
@@ -34,14 +35,17 @@
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.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.graphics.Color
-import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.LinkAnnotation
 import androidx.compose.ui.text.Placeholder
 import androidx.compose.ui.text.PlaceholderVerticalAlign
 import androidx.compose.ui.text.SpanStyle
-import androidx.compose.ui.text.UrlAnnotation
 import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextDecoration
@@ -49,13 +53,14 @@
 import androidx.compose.ui.text.withStyle
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import kotlin.random.Random
 
 private const val WebLink = "https://developer.android.com"
 private const val LongWebLink =
     "https://developer.android.com/design/ui/mobile/guides/foundations/system-bars"
 private const val PhoneUri = "tel:+123456789"
 
-@OptIn(ExperimentalTextApi::class)
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun Hyperlinks() {
     Column(
@@ -70,7 +75,7 @@
             val stringWithLink = buildAnnotatedString {
                 append("Example with a custom style ")
                 withStyle(SpanStyle(fontSize = 26.sp)) {
-                    withAnnotation(UrlAnnotation(WebLink)) { append("developer.android.com") }
+                    withAnnotation(LinkAnnotation.Url(WebLink)) { append("developer.android.com") }
                 }
                 append(" link and a phone number ")
                 withStyle(
@@ -79,9 +84,9 @@
                         textDecoration = TextDecoration.None
                     )
                 ) {
-                    withAnnotation(UrlAnnotation(PhoneUri)) { append("+1 (234) 567890") }
+                    withAnnotation(LinkAnnotation.Url(PhoneUri)) { append("+1 (234) 567890") }
                 }
-                append(" with a custom style.\n")
+                append(" with a custom style.")
             }
             Text(text = stringWithLink)
         }
@@ -89,19 +94,21 @@
         Sample("Long links") {
             val text = buildAnnotatedString {
                 append("Example that contains ")
-                withAnnotation(UrlAnnotation(LongWebLink)) {
+                withAnnotation(LinkAnnotation.Url(LongWebLink)) {
                     append("a very very very very very very long long long long link")
                 }
                 append(" followed by another long link ")
-                withAnnotation(UrlAnnotation(LongWebLink)) { append(LongWebLink) }
+                withAnnotation(LinkAnnotation.Url(LongWebLink)) { append(LongWebLink) }
             }
             Text(text)
         }
         Sample("Links with overlapped bounds") {
             val text = buildAnnotatedString {
-                withAnnotation(UrlAnnotation(LongWebLink)) { append("The first link") }
+                withAnnotation(LinkAnnotation.Url(LongWebLink)) { append("The first link") }
                 append(" immediately followed by ")
-                withAnnotation(UrlAnnotation(LongWebLink)) { append("the second quite long link") }
+                withAnnotation(LinkAnnotation.Url(LongWebLink)) {
+                    append("the second quite long link")
+                }
                 append(" so their bounds are overlapped")
             }
             Text(text)
@@ -109,14 +116,14 @@
         Sample("Link inside clickable text") {
             Text(buildAnnotatedString {
                 append("Clickable text with a ")
-                withAnnotation(UrlAnnotation(WebLink)) { append("developer.android.com") }
+                withAnnotation(LinkAnnotation.Url(WebLink)) { append("developer.android.com") }
                 append(" link.")
             }, Modifier.clickable { })
         }
         Sample("BasicText styling") {
             BasicText(buildAnnotatedString {
                 append("BasicText with ")
-                withAnnotation(UrlAnnotation(WebLink)) { append("developer.android.com") }
+                withAnnotation(LinkAnnotation.Url(WebLink)) { append("developer.android.com") }
                 append(" link.")
             })
         }
@@ -124,7 +131,7 @@
             SelectionContainer {
                 Text(buildAnnotatedString {
                     append("Selectable text with a ")
-                    withAnnotation(UrlAnnotation(WebLink)) { append("developer.android.com") }
+                    withAnnotation(LinkAnnotation.Url(WebLink)) { append("developer.android.com") }
                     append(" link.")
                 })
             }
@@ -140,10 +147,57 @@
                 append("A ")
                 appendInlineContent("box")
                 append(" inline content and a ")
-                withAnnotation(UrlAnnotation(WebLink)) { append("developer.android.com") }
+                withAnnotation(LinkAnnotation.Url(WebLink)) { append("developer.android.com") }
                 append(" link.")
             }, inlineContent = mapOf("box" to inlineTextContent))
         }
+        Sample("Invalid link not opened") {
+            BasicText(
+                buildAnnotatedString {
+                    append("Attached ")
+                    withAnnotation(LinkAnnotation.Url("asdf")) {
+                        withStyle(SpanStyle(textDecoration = TextDecoration.LineThrough)) {
+                            append("link")
+                        }
+                    }
+                    append(" is invalid and won't be opened.")
+                }
+            )
+        }
+        Sample("Clickable inside a text") {
+            var color by remember { mutableStateOf(Color.LightGray) }
+            var background by remember { mutableStateOf(Color.LightGray) }
+
+            BasicText(
+                buildAnnotatedString {
+                    append("Text contains ")
+                    withAnnotation(LinkAnnotation.Clickable("color")) {
+                        withStyle(SpanStyle(color = color)) {
+                            append("a variable color clickable")
+                        }
+                    }
+                    append(" and ")
+                    withAnnotation(LinkAnnotation.Clickable("background")) {
+                        withStyle(SpanStyle(background = background)) {
+                            append("a variable background clickable")
+                        }
+                    }
+                    append(" parts.")
+                },
+                onLinkClicked = { link ->
+                    (link as? LinkAnnotation.Clickable)?.let { clickable ->
+                        when (clickable.tag) {
+                            "color" -> {
+                                color = Color(Random.nextInt())
+                            }
+                            "background" -> {
+                                background = Color(Random.nextInt()).copy(alpha = 0.3f)
+                            }
+                        }
+                    }
+                }
+            )
+        }
     }
 }
 
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
index 4ffe31f..fffc487 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
@@ -24,9 +24,12 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.imePadding
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text2.BasicTextField2
 import androidx.compose.foundation.text2.input.TextFieldState
+import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.material.LocalContentColor
 import androidx.compose.material.LocalTextStyle
@@ -43,7 +46,12 @@
 
 @Composable
 fun DecorationBoxDemos() {
-    Column(Modifier.padding(16.dp)) {
+    Column(
+        Modifier
+            .imePadding()
+            .verticalScroll(rememberScrollState())
+            .padding(16.dp)
+    ) {
         TagLine(tag = "Simple Decoration w/ Label")
         SimpleDecorationWithLabel()
 
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/KeyboardActionsDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/KeyboardActionsDemos.kt
index f76a052..83470a3 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/KeyboardActionsDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/KeyboardActionsDemos.kt
@@ -21,6 +21,7 @@
 import androidx.compose.foundation.demos.text.fontSize8
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.imePadding
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.text.KeyboardActionScope
@@ -53,7 +54,7 @@
 fun KeyboardActionsDemos() {
     val snackbarHostState = remember { SnackbarHostState() }
     val coroutineScope = rememberCoroutineScope()
-    Box {
+    Box(Modifier.imePadding()) {
         var executeDefaultActions by remember { mutableStateOf(true) }
         val onImeAction: KeyboardActionScope.(ImeAction) -> Unit = remember(executeDefaultActions) {
             {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/KeyboardOptionsDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/KeyboardOptionsDemos.kt
index 3f50bd2..5d7286b 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/KeyboardOptionsDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/KeyboardOptionsDemos.kt
@@ -24,6 +24,7 @@
 import androidx.compose.foundation.demos.text.fontSize8
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.imePadding
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.shape.RoundedCornerShape
@@ -48,7 +49,7 @@
 @Preview
 @Composable
 fun KeyboardOptionsDemos() {
-    LazyColumn {
+    LazyColumn(Modifier.imePadding()) {
         item { Item(KeyboardType.Text) }
         item { Item(KeyboardType.Ascii) }
         item { Item(KeyboardType.Number) }
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/TextFieldLineLimitsDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/TextFieldLineLimitsDemos.kt
index bfd72d8..5754efb 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/TextFieldLineLimitsDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/TextFieldLineLimitsDemos.kt
@@ -20,10 +20,13 @@
 import androidx.compose.foundation.demos.text.TagLine
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.imePadding
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text2.BasicTextField2
 import androidx.compose.foundation.text2.input.TextFieldLineLimits
 import androidx.compose.foundation.text2.input.rememberTextFieldState
+import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.LocalTextStyle
 import androidx.compose.material.Slider
 import androidx.compose.material.Text
@@ -39,7 +42,12 @@
 
 @Composable
 fun TextFieldLineLimitsDemos() {
-    Column(Modifier.padding(16.dp)) {
+    Column(
+        Modifier
+            .imePadding()
+            .verticalScroll(rememberScrollState())
+            .padding(16.dp)
+    ) {
         TagLine(tag = "Default")
         DefaultLineLimits()
 
diff --git a/compose/foundation/foundation/lint-baseline.xml b/compose/foundation/foundation/lint-baseline.xml
index 6aa9e58..876590d 100644
--- a/compose/foundation/foundation/lint-baseline.xml
+++ b/compose/foundation/foundation/lint-baseline.xml
@@ -47,33 +47,6 @@
     </issue>
 
     <issue
-        id="IllegalExperimentalApiUsage"
-        message="`Experimental` and `RequiresOptIn` APIs may only be used within the same-version group where they were defined."
-        errorLine1="@OptIn(ExperimentalTextApi::class)"
-        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt"/>
-    </issue>
-
-    <issue
-        id="IllegalExperimentalApiUsage"
-        message="`Experimental` and `RequiresOptIn` APIs may only be used within the same-version group where they were defined."
-        errorLine1="@OptIn(ExperimentalTextApi::class)"
-        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt"/>
-    </issue>
-
-    <issue
-        id="IllegalExperimentalApiUsage"
-        message="`Experimental` and `RequiresOptIn` APIs may only be used within the same-version group where they were defined."
-        errorLine1="@OptIn(ExperimentalTextApi::class)"
-        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/foundation/text/TextLinkScope.kt"/>
-    </issue>
-
-    <issue
         id="PrimitiveInCollection"
         message="field anchors with type Map&lt;T, Float>: replace with ObjectFloatMap"
         errorLine1="    internal val anchors = mutableMapOf&lt;T, Float>()"
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextSample.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextSample.kt
new file mode 100644
index 0000000..81f72f5
--- /dev/null
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextSample.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.text.LinkAnnotation
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.withAnnotation
+
+@Composable
+@Sampled
+fun BasicTextWithLinks() {
+    Column {
+        BasicText(buildAnnotatedString {
+            withAnnotation(LinkAnnotation.Url("https://developer.android.com/")) {
+                append("Android for Developers")
+            }
+        })
+
+        @OptIn(ExperimentalFoundationApi::class)
+        BasicText(
+            text = buildAnnotatedString {
+                append("Click ")
+                withAnnotation(LinkAnnotation.Clickable("tag")) {
+                    append("here")
+                }
+            },
+            onLinkClicked = {
+                // do something with link
+            }
+        )
+    }
+}
+
+@Suppress("UNUSED_EXPRESSION")
+@Composable
+@Sampled
+fun BasicTextWithTextLinkClickHandler() {
+    val clickHandler = { link: LinkAnnotation ->
+        when (link) {
+            // do something with the link
+        }
+    }
+    @OptIn(ExperimentalFoundationApi::class)
+    BasicText(
+        text = buildAnnotatedString {
+            append("Click ")
+            withAnnotation(LinkAnnotation.Url("https://developer.android.com/")) {
+                append("Android for Developers")
+            }
+            append(" and ")
+            withAnnotation(LinkAnnotation.Clickable("tag")) {
+                append("other part")
+            }
+            append(" of this text.")
+        },
+        onLinkClicked = clickHandler
+    )
+}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
index 60a39394..e8c2e53 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
@@ -72,7 +72,9 @@
 import androidx.compose.ui.test.assertHasClickAction
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsFocused
 import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsNotFocused
 import androidx.compose.ui.test.assertTouchHeightIsEqualTo
 import androidx.compose.ui.test.assertTouchWidthIsEqualTo
 import androidx.compose.ui.test.assertWidthIsEqualTo
@@ -85,6 +87,7 @@
 import androidx.compose.ui.test.performMouseInput
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.pressKey
+import androidx.compose.ui.test.requestFocus
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
@@ -93,6 +96,7 @@
 import com.google.common.truth.Correspondence
 import com.google.common.truth.Truth.assertThat
 import kotlin.reflect.KClass
+import kotlin.test.Ignore
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 import org.junit.After
@@ -315,6 +319,117 @@
     }
 
     @Test
+    fun requestFocus_touchMode() {
+        // Arrange.
+        val tag = "testClickable"
+        val focusRequester = FocusRequester()
+        lateinit var inputModeManager: InputModeManager
+        rule.setContent {
+            inputModeManager = LocalInputModeManager.current
+            Box(
+                Modifier
+                    .testTag(tag)
+                    .size(10.dp)
+                    .focusRequester(focusRequester)
+                    .clickable {}
+            )
+        }
+        rule.runOnIdle {
+            @OptIn(ExperimentalComposeUiApi::class)
+            inputModeManager.requestInputMode(Touch)
+        }
+
+        // Act.
+        rule.runOnIdle { focusRequester.requestFocus() }
+
+        // Assert.
+        rule.onNodeWithTag(tag).assertIsNotFocused()
+    }
+
+    @Test
+    fun requestFocus_keyboardMode() {
+        // Arrange.
+        val tag = "testClickable"
+        val focusRequester = FocusRequester()
+        lateinit var inputModeManager: InputModeManager
+        rule.setContent {
+            inputModeManager = LocalInputModeManager.current
+            Box(
+                Modifier
+                    .testTag(tag)
+                    .size(10.dp)
+                    .focusRequester(focusRequester)
+                    .clickable {}
+            )
+        }
+        rule.runOnIdle {
+            @OptIn(ExperimentalComposeUiApi::class)
+            inputModeManager.requestInputMode(Keyboard)
+        }
+
+        // Act.
+        rule.runOnIdle { focusRequester.requestFocus() }
+
+        // Assert.
+        rule.onNodeWithTag(tag).assertIsFocused()
+    }
+
+    @Test
+    fun requestFocus_withTestApi_touchMode() {
+        // Arrange.
+        val tag = "testClickable"
+        lateinit var inputModeManager: InputModeManager
+        rule.setContent {
+            inputModeManager = LocalInputModeManager.current
+            Box(
+                Modifier
+                    .testTag(tag)
+                    .size(10.dp)
+                    .clickable {}
+            )
+        }
+        rule.runOnIdle {
+            @OptIn(ExperimentalComposeUiApi::class)
+            inputModeManager.requestInputMode(Touch)
+        }
+
+        // Act.
+        rule.onNodeWithTag(tag).requestFocus()
+
+        // Assert.
+        rule.onNodeWithTag(tag).assertIsNotFocused()
+    }
+
+    @Ignore("b/320786728")
+    @Test
+    fun requestFocus_withTestApi_keyboardMode() {
+        // Arrange.
+        val tag = "testClickable"
+        lateinit var inputModeManager: InputModeManager
+        val focusRequester = FocusRequester()
+        rule.setFocusableContent {
+            inputModeManager = LocalInputModeManager.current
+            Box(
+                Modifier
+                    .focusRequester(focusRequester)
+                    .testTag(tag)
+                    .size(10.dp)
+                    .clickable {}
+            )
+        }
+        rule.runOnIdle {
+            @OptIn(ExperimentalComposeUiApi::class)
+            inputModeManager.requestInputMode(Keyboard)
+        }
+
+        // Act.
+        rule.onNodeWithTag(tag).requestFocus()
+
+        // Assert.
+        rule.onNodeWithTag(tag).assertIsFocused()
+    }
+
+    @Test
     fun interactionSource_noScrollableContainer() {
         val interactionSource = MutableInteractionSource()
 
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/FocusableTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/FocusableTest.kt
index 80b1504..d1ebf73 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/FocusableTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/FocusableTest.kt
@@ -46,11 +46,14 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.input.InputMode
+import androidx.compose.ui.input.InputModeManager
 import androidx.compose.ui.layout.LocalPinnableContainer
 import androidx.compose.ui.layout.PinnableContainer
 import androidx.compose.ui.layout.PinnableContainer.PinnedHandle
 import androidx.compose.ui.layout.SubcomposeLayout
 import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.LocalInputModeManager
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assert
@@ -131,6 +134,112 @@
     }
 
     @Test
+    fun requestFocus_touchMode() {
+        // Arrange.
+        val focusRequester = FocusRequester()
+        lateinit var inputModeManager: InputModeManager
+        rule.setContent {
+            inputModeManager = LocalInputModeManager.current
+            Box(
+                Modifier
+                    .testTag(focusTag)
+                    .size(10.dp)
+                    .focusRequester(focusRequester)
+                    .focusable()
+            )
+        }
+        rule.runOnIdle {
+            @OptIn(ExperimentalComposeUiApi::class)
+            inputModeManager.requestInputMode(InputMode.Touch)
+        }
+
+        // Act.
+        rule.runOnIdle { focusRequester.requestFocus() }
+
+        // Assert.
+        rule.onNodeWithTag(focusTag).assertIsFocused()
+    }
+
+    @Test
+    fun requestFocus_keyboardMode() {
+        // Arrange.
+        val focusRequester = FocusRequester()
+        lateinit var inputModeManager: InputModeManager
+        rule.setContent {
+            inputModeManager = LocalInputModeManager.current
+            Box(
+                Modifier
+                    .testTag(focusTag)
+                    .size(10.dp)
+                    .focusRequester(focusRequester)
+                    .focusable()
+            )
+        }
+        rule.runOnIdle {
+            @OptIn(ExperimentalComposeUiApi::class)
+            inputModeManager.requestInputMode(InputMode.Keyboard)
+        }
+
+        // Act.
+        rule.runOnIdle { focusRequester.requestFocus() }
+
+        // Assert.
+        rule.onNodeWithTag(focusTag).assertIsFocused()
+    }
+
+    @Test
+    fun requestFocus_withTestApi_touchMode() {
+        // Arrange.
+        lateinit var inputModeManager: InputModeManager
+        rule.setContent {
+            inputModeManager = LocalInputModeManager.current
+            Box(
+                Modifier
+                    .testTag(focusTag)
+                    .size(10.dp)
+                    .focusable()
+            )
+        }
+        rule.runOnIdle {
+            @OptIn(ExperimentalComposeUiApi::class)
+            inputModeManager.requestInputMode(InputMode.Touch)
+        }
+
+        // Act.
+        rule.onNodeWithTag(focusTag).requestFocus()
+
+        // Assert.
+        rule.onNodeWithTag(focusTag).assertIsFocused()
+    }
+
+    @Test
+    fun requestFocus_withTestApi_keyboardMode() {
+        // Arrange.
+        lateinit var inputModeManager: InputModeManager
+        val focusRequester = FocusRequester()
+        rule.setFocusableContent {
+            inputModeManager = LocalInputModeManager.current
+            Box(
+                Modifier
+                    .focusRequester(focusRequester)
+                    .testTag(focusTag)
+                    .size(10.dp)
+                    .focusable()
+            )
+        }
+        rule.runOnIdle {
+            @OptIn(ExperimentalComposeUiApi::class)
+            inputModeManager.requestInputMode(InputMode.Keyboard)
+        }
+
+        // Act.
+        rule.onNodeWithTag(focusTag).requestFocus()
+
+        // Assert.
+        rule.onNodeWithTag(focusTag).assertIsFocused()
+    }
+
+    @Test
     fun focusable_focusAcquire() {
         val (focusRequester, otherFocusRequester) = FocusRequester.createRefs()
         rule.setFocusableContent {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt
index 4690962..d96f5fc 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.foundation.gestures.TargetedFlingBehavior
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
@@ -491,7 +492,7 @@
 
 @OptIn(ExperimentalFoundationApi::class)
 internal class QuerySnapFlingBehavior(
-    val snapFlingBehavior: SnapFlingBehavior,
+    val snapFlingBehavior: TargetedFlingBehavior,
     val onAnimationStep: (Float) -> Unit
 ) : FlingBehavior {
     override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehaviorTest.kt
index c98f55d..3cfc45f 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehaviorTest.kt
@@ -29,6 +29,7 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.TestScrollMotionDurationScale
 import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.TargetedFlingBehavior
 import androidx.compose.foundation.gestures.rememberScrollableState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
@@ -78,7 +79,7 @@
     fun remainingScrollOffset_cannotApproach_shouldRepresentJustSnappingOffsets() {
         val approachOffset = 0.0f
         val testLayoutInfoProvider = TestLayoutInfoProvider(approachOffset = approachOffset)
-        lateinit var testFlingBehavior: SnapFlingBehavior
+        lateinit var testFlingBehavior: TargetedFlingBehavior
         val scrollOffset = mutableListOf<Float>()
         rule.setContent {
             testFlingBehavior = rememberSnapFlingBehavior(testLayoutInfoProvider)
@@ -103,7 +104,7 @@
         val approachOffset = 50f
         val testLayoutInfoProvider =
             TestLayoutInfoProvider(approachOffset = approachOffset)
-        lateinit var testFlingBehavior: SnapFlingBehavior
+        lateinit var testFlingBehavior: TargetedFlingBehavior
         val scrollOffset = mutableListOf<Float>()
         rule.setContent {
             testFlingBehavior = rememberSnapFlingBehavior(testLayoutInfoProvider)
@@ -128,7 +129,7 @@
         val initialOffset = 50f
         val testLayoutInfoProvider =
             TestLayoutInfoProvider(approachOffset = initialOffset)
-        lateinit var testFlingBehavior: SnapFlingBehavior
+        lateinit var testFlingBehavior: TargetedFlingBehavior
         val scrollOffset = mutableListOf<Float>()
         rule.mainClock.autoAdvance = false
         rule.setContent {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt
index 50bc6d8..f22a59c 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.text
 
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.focusable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -54,15 +55,16 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onAllNodesWithText
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performClick
 import androidx.compose.ui.test.performTouchInput
-import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.LinkAnnotation
+import androidx.compose.ui.text.LinkAnnotation.Url
 import androidx.compose.ui.text.Placeholder
 import androidx.compose.ui.text.PlaceholderVerticalAlign
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.UrlAnnotation
 import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.text.withAnnotation
 import androidx.compose.ui.text.withStyle
@@ -77,8 +79,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalFoundationApi::class)
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 @MediumTest
 class BasicTextLinkTest {
     @get:Rule
@@ -228,7 +230,7 @@
              * +--------------------+
              */
             val text = buildAnnotatedString {
-                withAnnotation(UrlAnnotation(Url1)) { append("link") }
+                withAnnotation(Url(Url1)) { append("link") }
                 append(" text ")
                 appendInlineContent("box")
                 append(" text")
@@ -264,7 +266,7 @@
     fun link_withTranslatedString() {
         val originalText = buildAnnotatedString {
             append("text ")
-            withAnnotation(UrlAnnotation(Url1)) {
+            withAnnotation(Url(Url1)) {
                 append("link")
             }
         }
@@ -306,7 +308,7 @@
                 val color = remember { mutableStateOf(Color.Red) }
                 BasicText(
                     buildAnnotatedString {
-                        withAnnotation(UrlAnnotation(Url1)) {
+                        withAnnotation(Url(Url1)) {
                             withStyle(SpanStyle(color = color.value)) {
                                 append("link")
                             }
@@ -328,6 +330,67 @@
         rule.onNode(hasClickAction()).assertIsFocused()
     }
 
+    @Test
+    fun link_handler_calledWithoutDefaultBehavior() {
+        var counter = 0
+        setupContent {
+            BasicText(
+                text = buildAnnotatedString {
+                    withAnnotation(Url(Url1)) { append("link") }
+                },
+                onLinkClicked = {
+                    counter++
+                }
+            )
+        }
+
+        rule.onNodeWithText("link").performClick()
+
+        rule.runOnIdle {
+            assertThat(openedUri).isNull()
+            assertThat(counter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun link_nullHandler_defaultBehavior() {
+        setupContent {
+            BasicText(
+                text = buildAnnotatedString {
+                    withAnnotation(Url(Url1)) { append("link") }
+                },
+                onLinkClicked = null // default
+            )
+        }
+
+        rule.onNodeWithText("link").performClick()
+
+        rule.runOnIdle {
+            assertThat(openedUri).isEqualTo(Url1)
+        }
+    }
+
+    @Test
+    fun clickable_handler_called() {
+        var counter = 0
+        setupContent {
+            BasicText(
+                text = buildAnnotatedString {
+                    withAnnotation(LinkAnnotation.Clickable(Url1)) { append("clickable") }
+                },
+                onLinkClicked = {
+                    counter++
+                }
+            )
+        }
+
+        rule.onNodeWithText("clickable").performClick()
+
+        rule.runOnIdle {
+            assertThat(counter).isEqualTo(1)
+        }
+    }
+
     @Composable
     private fun TextWithLinks() = with(rule.density) {
         Column {
@@ -343,11 +406,11 @@
 
             val text = buildAnnotatedString {
                 append("text ")
-                withAnnotation(UrlAnnotation(Url1)) {
+                withAnnotation(Url(Url1)) {
                     append("link ")
                 }
                 append("text ")
-                withAnnotation(UrlAnnotation(Url2)) {
+                withAnnotation(Url(Url2)) {
                     append("a long link ")
                 }
                 append("text")
@@ -362,7 +425,7 @@
 
             BasicText(buildAnnotatedString {
                 append("text ")
-                withAnnotation(UrlAnnotation(Url3)) {
+                withAnnotation(Url(Url3)) {
                     append("link ")
                 }
             }, style = style)
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text2/input/internal/selection/TextFieldTextToolbarTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text2/input/internal/selection/TextFieldTextToolbarTest.kt
index c428baa..7dd9f63 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text2/input/internal/selection/TextFieldTextToolbarTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text2/input/internal/selection/TextFieldTextToolbarTest.kt
@@ -986,7 +986,7 @@
         }
     }
 
-    override fun setClip(clipEntry: ClipEntry, clipMetadata: ClipMetadata?) {
+    override fun setClip(clipEntry: ClipEntry) {
         if (supportsClipEntry) {
             currentClipEntry = clipEntry
         } else {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldUndoTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldUndoTest.kt
index 41f39d6..7f30a0d 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldUndoTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldUndoTest.kt
@@ -16,20 +16,16 @@
 
 package androidx.compose.foundation.textfield
 
-import android.view.KeyEvent
-import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.runtime.mutableStateOf
-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.KeyEvent as ComposeKeyEvent
-import androidx.compose.ui.input.key.nativeKeyCode
+import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.hasSetTextAction
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.performKeyPress
+import androidx.compose.ui.test.performKeyInput
+import androidx.compose.ui.test.pressKey
+import androidx.compose.ui.test.requestFocus
+import androidx.compose.ui.test.withKeyDown
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
@@ -39,66 +35,56 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalFoundationApi::class)
+@OptIn(ExperimentalTestApi::class)
 class TextFieldUndoTest {
+
     @get:Rule
     val rule = createComposeRule()
 
-    @OptIn(ExperimentalComposeUiApi::class)
     @Test
-    fun undo_redo() {
+    fun undo_redo_withCtrlShiftZ() {
+        undoRedoTest(redoKeys = listOf(Key.CtrlLeft, Key.ShiftLeft, Key.Z))
+    }
+
+    @Test
+    fun undo_redo_withCtrlY() {
+        undoRedoTest(redoKeys = listOf(Key.CtrlLeft, Key.Y))
+    }
+
+    private fun undoRedoTest(redoKeys: List<Key>) {
         val state = mutableStateOf("hi")
-        val focusFequester = FocusRequester()
         rule.setContent {
             BasicTextField(
                 value = state.value,
-                modifier = Modifier.focusRequester(focusFequester),
-                onValueChange = {
-                    state.value = it
-                }
+                onValueChange = { state.value = it }
             )
         }
 
-        rule.runOnIdle { focusFequester.requestFocus() }
-
         state.value = "hello"
 
-        rule.waitForIdle()
-
-        fun verifyRedoShortcut(redoKeyEvent: ComposeKeyEvent) {
-            // undo command
-            rule.onNode(hasSetTextAction()).performKeyPress(downEvent(Key.Z, KeyEvent.META_CTRL_ON))
-
-            rule.runOnIdle {
-                assertThat(state.value).isEqualTo("hi")
-            }
-
-            // redo command
-            rule.onNode(hasSetTextAction()).performKeyPress(redoKeyEvent)
-
-            rule.runOnIdle {
-                assertThat(state.value).isEqualTo("hello")
+        // undo command
+        with(rule.onNode(hasSetTextAction())) {
+            requestFocus()
+            performKeyInput {
+                withKeyDown(Key.CtrlLeft) {
+                    pressKey(Key.Z)
+                }
             }
         }
 
-        verifyRedoShortcut(
-            downEvent(
-                Key.Z,
-                KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON
-            )
-        )
+        rule.runOnIdle {
+            assertThat(state.value).isEqualTo("hi")
+        }
 
-        verifyRedoShortcut(
-            downEvent(
-                Key.Y,
-                KeyEvent.META_CTRL_ON
-            )
-        )
+        // redo command
+        rule.onNode(hasSetTextAction()).performKeyInput {
+            redoKeys.forEach { keyDown(it) }
+            advanceEventTime(100)
+            redoKeys.forEach { keyUp(it) }
+        }
+
+        rule.runOnIdle {
+            assertThat(state.value).isEqualTo("hello")
+        }
     }
 }
-
-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)
-    )
-}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/TextLinkClickHandler.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/TextLinkClickHandler.kt
new file mode 100644
index 0000000..7bcfc442
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/TextLinkClickHandler.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation
+
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.text.LinkAnnotation
+
+/**
+ * A handler that will be called when a user clicks a link from the text that is represented as a
+ * [LinkAnnotation] annotation.
+ *
+ * If you need to make part of the text clickable and get notified when users clicks it, pass this
+ * handler to the [BasicText] composable function.
+ *
+ * Note that if you pass this handler to the [BasicText], you will need to handle opening the url
+ * manually. To do so use [androidx.compose.ui.platform.LocalUriHandler] composition local.
+ *
+ * @sample androidx.compose.foundation.samples.BasicTextWithTextLinkClickHandler
+ */
+@ExperimentalFoundationApi
+@Stable
+fun interface TextLinkClickHandler {
+
+    /**
+     * Called when a corresponding [link] is clicked by a user.
+     */
+    fun onClick(link: LinkAnnotation)
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TargetedFlingBehavior.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TargetedFlingBehavior.kt
new file mode 100644
index 0000000..a63253a
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TargetedFlingBehavior.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.gestures
+
+import androidx.compose.runtime.Stable
+
+/**
+ * Interface to specify fling behavior with additional information about its animation target.
+ */
+@Stable
+interface TargetedFlingBehavior : FlingBehavior {
+
+    /**
+     * Perform settling via fling animation with given velocity and suspend until fling has
+     * finished. Use [onRemainingDistanceUpdated] to report the status of the ongoing fling
+     * animation and the remaining amount of scroll offset.
+     *
+     * This functions is called with [ScrollScope] to drive the state change of the
+     * [androidx.compose.foundation.gestures.ScrollableState] via [ScrollScope.scrollBy].
+     *
+     * This function must return the correct velocity left after it is finished flinging in order
+     * to guarantee proper nested scroll support.
+     *
+     * @param initialVelocity velocity available for fling in the orientation specified in
+     * [androidx.compose.foundation.gestures.scrollable] that invoked this method.
+     * @param onRemainingDistanceUpdated a lambda that will be called anytime the
+     * distance to the settling offset is updated. The settling offset is the final offset where
+     * this fling will stop and may change depending on the snapping animation progression.
+     *
+     * @return remaining velocity after fling operation has ended
+     */
+    suspend fun ScrollScope.performFling(
+        initialVelocity: Float,
+        onRemainingDistanceUpdated: (Float) -> Unit
+    ): Float
+
+    override suspend fun ScrollScope.performFling(initialVelocity: Float): Float =
+        performFling(initialVelocity, NoOnReport)
+}
+
+private val NoOnReport: (Float) -> Unit = { }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
index e1aef6f..a0fa6f7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
@@ -33,6 +33,7 @@
 import androidx.compose.foundation.gestures.DefaultScrollMotionDurationScale
 import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.foundation.gestures.TargetedFlingBehavior
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalDensity
@@ -43,7 +44,7 @@
 import kotlinx.coroutines.withContext
 
 /**
- * A [FlingBehavior] that performs snapping of items to a given position.
+ * A [TargetedFlingBehavior] that performs snapping of items to a given position.
  *
  * You can use [SnapLayoutInfoProvider.calculateApproachOffset] to
  * indicate that snapping should happen after this offset. If the velocity generated by the
@@ -69,7 +70,7 @@
     private val snapLayoutInfoProvider: SnapLayoutInfoProvider,
     private val decayAnimationSpec: DecayAnimationSpec<Float>,
     private val snapAnimationSpec: AnimationSpec<Float>
-) : FlingBehavior {
+) : TargetedFlingBehavior {
 
     /**
      * A [FlingBehavior] that performs snapping of items to a given position. The algorithm will
@@ -116,29 +117,25 @@
 
     internal var motionScaleDuration = DefaultScrollMotionDurationScale
 
-    override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
-        return performFling(initialVelocity) {}
-    }
-
     /**
      * Perform a snapping fling animation with given velocity and suspend until fling has
      * finished. This will behave the same way as [performFling] except it will report on
-     * each remainingOffsetUpdate using the [onSettlingDistanceUpdated] lambda.
+     * each remainingOffsetUpdate using the [onRemainingDistanceUpdated] lambda.
      *
      * @param initialVelocity velocity available for fling in the orientation specified in
      * [androidx.compose.foundation.gestures.scrollable] that invoked this method.
      *
-     * @param onSettlingDistanceUpdated a lambda that will be called anytime the
+     * @param onRemainingDistanceUpdated a lambda that will be called anytime the
      * distance to the settling offset is updated. The settling offset is the final offset where
      * this fling will stop and may change depending on the snapping animation progression.
      *
      * @return remaining velocity after fling operation has ended
      */
-    suspend fun ScrollScope.performFling(
+    override suspend fun ScrollScope.performFling(
         initialVelocity: Float,
-        onSettlingDistanceUpdated: (Float) -> Unit
+        onRemainingDistanceUpdated: (Float) -> Unit
     ): Float {
-        val (remainingOffset, remainingState) = fling(initialVelocity, onSettlingDistanceUpdated)
+        val (remainingOffset, remainingState) = fling(initialVelocity, onRemainingDistanceUpdated)
 
         debugLog { "Post Settling Offset=$remainingOffset" }
         // No remaining offset means we've used everything, no need to propagate velocity. Otherwise
@@ -318,7 +315,7 @@
 @Composable
 fun rememberSnapFlingBehavior(
     snapLayoutInfoProvider: SnapLayoutInfoProvider
-): SnapFlingBehavior {
+): TargetedFlingBehavior {
     val density = LocalDensity.current
     val highVelocityApproachSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay()
     return remember(
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 6089a6c..da55ebb 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
@@ -16,6 +16,8 @@
 
 package androidx.compose.foundation.text
 
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.TextLinkClickHandler
 import androidx.compose.foundation.text.modifiers.SelectableTextAnnotatedStringElement
 import androidx.compose.foundation.text.modifiers.SelectionController
 import androidx.compose.foundation.text.modifiers.TextAnnotatedStringElement
@@ -46,7 +48,6 @@
 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
@@ -177,7 +178,6 @@
  * used to insert composables into text layout. Check [InlineTextContent] for more information.
  * @param color Overrides the text color provided in [style]
  */
-@OptIn(ExperimentalTextApi::class)
 @Composable
 fun BasicText(
     text: AnnotatedString,
@@ -191,6 +191,74 @@
     inlineContent: Map<String, InlineTextContent> = mapOf(),
     color: ColorProducer? = null
 ) {
+    @OptIn(ExperimentalFoundationApi::class)
+    BasicText(
+        text,
+        modifier,
+        style,
+        onTextLayout,
+        overflow,
+        softWrap,
+        maxLines,
+        minLines,
+        inlineContent,
+        color,
+        null
+    )
+}
+
+/**
+ * Basic element that displays text and provides semantics / accessibility information.
+ * Typically you will instead want to use [androidx.compose.material.Text], which is
+ * a higher level Text element that contains semantics and consumes style information from a theme.
+ *
+ * To display hyperlinks in text, see example below
+ * @sample androidx.compose.foundation.samples.BasicTextWithLinks
+ *
+ * @param text The text to be displayed.
+ * @param modifier [Modifier] to apply to this layout node.
+ * @param style Style configuration for the text such as color, font, line height etc.
+ * @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 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]. It is required that 1 <= [minLines] <= [maxLines].
+ * @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 color Overrides the text color provided in [style]
+ * @param onLinkClicked a handler that is called when a
+ * (link)[androidx.compose.ui.text.LinkAnnotation] inside the text is clicked. If you need to make
+ * part of a text clickable, you can mark that part as a
+ * (Url)[androidx.compose.ui.text.LinkAnnotation.Url] or
+ * (Clickable)[androidx.compose.ui.text.LinkAnnotation.Clickable]. When a user will click on it,
+ * this handler will be triggered. Note that when null is passed, a default link handling mechanism
+ * will be triggered. Which is for (Url)[androidx.compose.ui.text.LinkAnnotation.Url] the system
+ * will try to open the corresponding url and for
+ * (Clickable)[androidx.compose.ui.text.LinkAnnotation.Clickable] it will be a no-op.
+ */
+@ExperimentalFoundationApi
+@Composable
+fun BasicText(
+    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> = mapOf(),
+    color: ColorProducer? = null,
+    onLinkClicked: TextLinkClickHandler? = null
+) {
     validateMinMaxLines(
         minLines = minLines,
         maxLines = maxLines
@@ -262,7 +330,8 @@
                 } else {
                     substitutionValue.original
                 }
-            }
+            },
+            linkClickHandler = onLinkClicked
         )
     }
 }
@@ -516,6 +585,7 @@
     }
 }
 
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
 private fun LayoutWithLinksAndInlineContent(
     modifier: Modifier,
@@ -531,10 +601,11 @@
     fontFamilyResolver: FontFamily.Resolver,
     selectionController: SelectionController?,
     color: ColorProducer?,
-    onShowTranslation: ((TextAnnotatedStringNode.TextSubstitutionValue) -> Unit)?
+    onShowTranslation: ((TextAnnotatedStringNode.TextSubstitutionValue) -> Unit)?,
+    linkClickHandler: TextLinkClickHandler?
 ) {
     val textScope = if (text.hasLinks()) {
-        remember(text) { TextLinkScope(text) }
+        remember(text, linkClickHandler) { TextLinkScope(text, linkClickHandler) }
     } else null
 
     // do the inline content allocs
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLinkScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLinkScope.kt
index b4a1886..631e0e1 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLinkScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLinkScope.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.LocalIndication
+import androidx.compose.foundation.TextLinkClickHandler
 import androidx.compose.foundation.combinedClickable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
@@ -31,12 +32,14 @@
 import androidx.compose.ui.graphics.Outline
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.input.pointer.PointerIcon
+import androidx.compose.ui.input.pointer.pointerHoverIcon
 import androidx.compose.ui.layout.ParentDataModifier
 import androidx.compose.ui.platform.LocalUriHandler
+import androidx.compose.ui.platform.UriHandler
 import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.LinkAnnotation
 import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.UrlAnnotation
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.LayoutDirection
@@ -44,8 +47,7 @@
 import androidx.compose.ui.util.fastForEach
 import kotlin.math.min
 
-@OptIn(ExperimentalTextApi::class)
-internal typealias LinkRange = AnnotatedString.Range<UrlAnnotation>
+internal typealias LinkRange = AnnotatedString.Range<LinkAnnotation>
 
 /**
  * A scope that provides necessary information to attach a hyperlink to the text range.
@@ -53,8 +55,11 @@
  * This class assumes that links exist and does not perform any additional check inside its methods.
  * Therefore this class initialisation should be guarded by the `hasLinks` check.
  */
-@OptIn(ExperimentalTextApi::class)
-internal class TextLinkScope(val text: AnnotatedString) {
+@OptIn(ExperimentalFoundationApi::class)
+internal class TextLinkScope(
+    val text: AnnotatedString,
+    private val linkClickHandler: TextLinkClickHandler?
+) {
     var textLayoutResult: TextLayoutResult? by mutableStateOf(null)
 
     // indicates whether the links should be measured or not. The latter needed to handle
@@ -124,19 +129,44 @@
         val indication = LocalIndication.current
         val uriHandler = LocalUriHandler.current
 
-        val links = text.getUrlAnnotations(0, text.length)
+        val links = text.getLinkAnnotations(0, text.length)
         links.fastForEach { range ->
             val shape = shapeForRange(range)
             val clipModifier = shape?.let { Modifier.clip(it) } ?: Modifier
             Box(
                 clipModifier
                     .textRange(range.start, range.end)
+                    .pointerHoverIcon(PointerIcon.Hand)
                     .combinedClickable(null, indication, onClick = {
-                        uriHandler.openUri(range.item.url)
+                        handleLink(range.item, uriHandler, linkClickHandler)
                     })
             )
         }
     }
+
+    private fun handleLink(
+        link: LinkAnnotation,
+        uriHandler: UriHandler,
+        clickHandler: TextLinkClickHandler?
+    ) {
+        when (link) {
+            is LinkAnnotation.Url -> {
+                // if a handler is present, we delegate link handling to it. If not, we try to
+                // handle links ourselves. And if we can't (the uri is invalid or there's no app to
+                // handle such a uri), we silently fail
+                clickHandler?.onClick(link)
+                    ?: try {
+                        uriHandler.openUri(link.url)
+                    } catch (_: IllegalArgumentException) {
+                        // we choose to silently fail when the uri can't be opened to avoid crashes
+                        // for users. This is the case where developer don't provide the link
+                        // handlers themselves and therefore I suspect are less likely to test them
+                        // manually.
+                    }
+            }
+            is LinkAnnotation.Clickable -> clickHandler?.onClick(link)
+        }
+    }
 }
 
 /**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
index 23aa7d7..9766ec8 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
@@ -55,7 +55,6 @@
 import androidx.compose.ui.semantics.text
 import androidx.compose.ui.semantics.textSubstitution
 import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.Placeholder
 import androidx.compose.ui.text.TextLayoutInput
 import androidx.compose.ui.text.TextLayoutResult
@@ -559,6 +558,4 @@
     }
 }
 
-@OptIn(ExperimentalTextApi::class)
-// TODO(soboleva) replace with has*Annotations in upcoming CL with API change
-internal fun AnnotatedString.hasLinks() = getUrlAnnotations(0, text.length).isNotEmpty()
+internal fun AnnotatedString.hasLinks() = hasLinkAnnotations(0, length)
diff --git a/compose/integration-tests/macrobenchmark-target/build.gradle b/compose/integration-tests/macrobenchmark-target/build.gradle
index f3bc589..edd93da78 100644
--- a/compose/integration-tests/macrobenchmark-target/build.gradle
+++ b/compose/integration-tests/macrobenchmark-target/build.gradle
@@ -47,6 +47,8 @@
     implementation(project(":compose:ui:ui"))
     implementation(project(":compose:ui:ui-tooling"))
     implementation(project(":profileinstaller:profileinstaller"))
+    implementation(project(":constraintlayout:constraintlayout-compose"))
+    implementation("io.coil-kt:coil-compose:2.5.0")
 }
 
 android.defaultConfig.minSdkVersion 21
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
index d54a8f1..d56e2a2 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -17,6 +17,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools">
 
+    <uses-permission android:name="android.permission.INTERNET"/>
+
     <application
         android:label="Jetpack Compose Macrobenchmark Target"
         android:allowBackup="false"
@@ -109,6 +111,18 @@
             </intent-filter>
         </activity>
         <activity
+            android:name=".ComplexNestedListsActivity"
+            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="androidx.compose.integration.macrobenchmark.target.COMPLEX_NESTED_LISTS_ACTIVITY" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity
             android:name=".LazyBoxWithConstraintsActivity"
             android:exported="true">
             <intent-filter>
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/ComplexNestedListsActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/ComplexNestedListsActivity.kt
new file mode 100644
index 0000000..cab2d3d
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/ComplexNestedListsActivity.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.Choreographer
+import android.view.View
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.aspectRatio
+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.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.AccountBox
+import androidx.compose.material3.Card
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Recomposer
+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.graphics.Color
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.Dimension
+import coil.compose.AsyncImage
+
+class ComplexNestedListsActivity : ComponentActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContent {
+            MaterialTheme {
+                // A surface container using the 'background' color from the theme
+                Surface(
+                    modifier = Modifier.fillMaxSize(),
+                    color = MaterialTheme.colors.background
+                ) {
+                    Greeting()
+                }
+            }
+        }
+
+        launchIdlenessTracking()
+    }
+
+    internal fun ComponentActivity.launchIdlenessTracking() {
+        val contentView: View = findViewById(android.R.id.content)
+        val callback: Choreographer.FrameCallback = object : Choreographer.FrameCallback {
+            override fun doFrame(frameTimeNanos: Long) {
+                if (Recomposer.runningRecomposers.value.any { it.hasPendingWork }) {
+                    contentView.contentDescription = "COMPOSE-BUSY"
+                } else {
+                    contentView.contentDescription = "COMPOSE-IDLE"
+                }
+                Choreographer.getInstance().postFrameCallback(this)
+            }
+        }
+        Choreographer.getInstance().postFrameCallback(callback)
+    }
+}
+
+@Composable
+private fun Greeting() {
+    LazyColumn(Modifier.semantics { contentDescription = "IamLazy" }) {
+        items(1000) {
+            LazyRow {
+                items(10) {
+                    Video(
+                        modifier = Modifier
+                            .width(200.dp)
+                            .height(120.dp)
+                            .padding(16.dp)
+                    )
+                }
+            }
+            Box(
+                modifier = Modifier
+                    .height(1.dp)
+                    .fillMaxWidth()
+                    .background(Color.Black)
+            )
+        }
+    }
+}
+
+@Composable
+private fun Video(
+    modifier: Modifier = Modifier,
+    imageRes: Int = R.drawable.simple_image,
+    duration: String = "100",
+    onVideoClick: () -> Unit = {},
+    shimmerModifier: Modifier = Modifier
+) {
+    Column(
+        modifier = modifier.clickable(
+            interactionSource = remember { MutableInteractionSource() },
+            indication = null,
+            onClick = onVideoClick
+        )
+    ) {
+        VideoImageBox(
+            modifier = Modifier.then(shimmerModifier),
+            imageRes = imageRes,
+            duration = duration
+        )
+    }
+}
+
+@Composable
+private fun VideoImageBox(
+    modifier: Modifier,
+    imageRes: Int,
+    duration: String,
+) {
+    Card(
+        modifier = modifier
+            .aspectRatio(16f / 9)
+            .shadow(
+                elevation = 12.dp,
+                spotColor = Color.Gray,
+                shape = RoundedCornerShape(size = 12.dp)
+            )
+    ) {
+        ConstraintLayout(Modifier.fillMaxSize()) {
+            val (image, durationBox) = createRefs()
+
+            AsyncImage(
+                modifier = Modifier.constrainAs(image) {
+                    top.linkTo(parent.top)
+                    bottom.linkTo(parent.bottom)
+                    start.linkTo(parent.start)
+                    end.linkTo(parent.end)
+                    width = Dimension.fillToConstraints
+                    height = Dimension.fillToConstraints
+                },
+                model = imageRes,
+                contentDescription = null,
+                contentScale = ContentScale.Crop
+            )
+
+            Row(
+                modifier = Modifier.constrainAs(durationBox) {
+                    bottom.linkTo(parent.bottom)
+                    end.linkTo(parent.end)
+                    width = Dimension.wrapContent
+                    height = Dimension.wrapContent
+                },
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                Text(
+                    text = duration,
+                    color = Color.White,
+                )
+                Spacer(modifier = Modifier.width(2.dp))
+                Icon(
+                    modifier = Modifier
+                        .size(12.dp)
+                        .padding(2.dp),
+                    imageVector = Icons.Default.AccountBox,
+                    contentDescription = null,
+                    tint = Color.White
+                )
+            }
+        }
+    }
+}
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/res/drawable-nodpi/simple_image.jpg b/compose/integration-tests/macrobenchmark-target/src/main/res/drawable-nodpi/simple_image.jpg
new file mode 100644
index 0000000..d2233a7
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark-target/src/main/res/drawable-nodpi/simple_image.jpg
Binary files differ
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/ComplexNestedListsScrollBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/ComplexNestedListsScrollBenchmark.kt
new file mode 100644
index 0000000..003b307
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/ComplexNestedListsScrollBenchmark.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 android.graphics.Point
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class ComplexNestedListsScrollBenchmark {
+    @get:Rule
+    val benchmarkRule = MacrobenchmarkRule()
+
+    private lateinit var device: UiDevice
+
+    @Before
+    fun setUp() {
+        val instrumentation = InstrumentationRegistry.getInstrumentation()
+        device = UiDevice.getInstance(instrumentation)
+    }
+
+    @Test
+    fun start() {
+        benchmarkRule.measureRepeated(
+            packageName = PACKAGE_NAME,
+            metrics = listOf(FrameTimingMetric()),
+            compilationMode = CompilationMode.Full(),
+            iterations = 8,
+            setupBlock = {
+                val intent = Intent()
+                intent.action = ACTION
+                startActivityAndWait(intent)
+            }
+        ) {
+            val lazyColumn = device.findObject(By.desc(CONTENT_DESCRIPTION))
+            // Setting a gesture margin is important otherwise gesture nav is triggered.
+            lazyColumn.setGestureMargin(device.displayWidth / 5)
+            for (i in 1..10) {
+                // From center we scroll 2/3 of it which is 1/3 of the screen.
+                lazyColumn.drag(Point(lazyColumn.visibleCenter.x, lazyColumn.visibleCenter.y / 3))
+                device.wait(Until.findObject(By.desc(COMPOSE_IDLE)), 3000)
+            }
+        }
+    }
+
+    companion object {
+        private const val PACKAGE_NAME = "androidx.compose.integration.macrobenchmark.target"
+        private const val ACTION =
+            "androidx.compose.integration.macrobenchmark.target.COMPLEX_NESTED_LISTS_ACTIVITY"
+        private const val CONTENT_DESCRIPTION = "IamLazy"
+
+        private const val COMPOSE_IDLE = "COMPOSE-IDLE"
+    }
+}
diff --git a/compose/integration-tests/material-catalog/build.gradle b/compose/integration-tests/material-catalog/build.gradle
index 3af9784..97c2ce3 100644
--- a/compose/integration-tests/material-catalog/build.gradle
+++ b/compose/integration-tests/material-catalog/build.gradle
@@ -52,7 +52,7 @@
     implementation project(":compose:runtime:runtime")
     implementation project(":compose:foundation:foundation-layout")
     implementation project(":compose:ui:ui")
-    implementation("androidx.compose.material:material:1.6.0-beta01")
+    implementation project(":compose:material:material")
     implementation project(":compose:material3:material3")
     implementation project(":compose:material:material:integration-tests:material-catalog")
     implementation project(":compose:material3:material3:integration-tests:material3-catalog")
diff --git a/compose/material/material-ripple/build.gradle b/compose/material/material-ripple/build.gradle
index e40a2e1..026f271 100644
--- a/compose/material/material-ripple/build.gradle
+++ b/compose/material/material-ripple/build.gradle
@@ -43,7 +43,7 @@
                 api(project(":compose:foundation:foundation"))
                 api(project(":compose:runtime:runtime"))
 
-                implementation(project(":collection:collection"))
+                implementation("androidx.collection:collection:1.4.0")
                 implementation(project(":compose:animation:animation"))
                 implementation(project(":compose:ui:ui-util"))
             }
diff --git a/compose/material/material/build.gradle b/compose/material/material/build.gradle
index 692fbd3..df79395 100644
--- a/compose/material/material/build.gradle
+++ b/compose/material/material/build.gradle
@@ -83,7 +83,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api("androidx.annotation:annotation:1.1.0")
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
 
                 // TODO: remove next 3 dependencies when b/202810604 is fixed
                 implementation("androidx.savedstate:savedstate:1.2.1")
diff --git a/compose/material/material/integration-tests/material-catalog/build.gradle b/compose/material/material/integration-tests/material-catalog/build.gradle
index 26c868d..ba6ac0a 100644
--- a/compose/material/material/integration-tests/material-catalog/build.gradle
+++ b/compose/material/material/integration-tests/material-catalog/build.gradle
@@ -36,8 +36,8 @@
     implementation project(":compose:runtime:runtime")
     implementation project(":compose:foundation:foundation-layout")
     implementation project(":compose:ui:ui")
-    implementation("androidx.compose.material:material:1.6.0-beta01")
-    implementation("androidx.compose.material:material-samples:1.6.0-beta01")
+    implementation project(":compose:material:material")
+    implementation project(":compose:material:material:material-samples")
     implementation project(":navigation:navigation-compose")
 }
 
diff --git a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt
index 9c8f953..22dfdd9 100644
--- a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt
+++ b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt
@@ -36,7 +36,7 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog as CoreDialog
+import androidx.compose.ui.window.DialogWindow
 import androidx.compose.ui.window.Popup
 import androidx.compose.ui.window.PopupPositionProvider
 import androidx.compose.ui.window.rememberDialogState
@@ -229,7 +229,7 @@
         onDismissRequest: () -> Unit,
         content: @Composable () -> Unit
     ) {
-        CoreDialog(
+        DialogWindow(
             onCloseRequest = onDismissRequest,
             state = rememberDialogState(width = Dp.Unspecified, height = Dp.Unspecified),
             undecorated = true,
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SearchBarBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SearchBarBenchmark.kt
new file mode 100644
index 0000000..407bcfa
--- /dev/null
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SearchBarBenchmark.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.benchmark
+
+import androidx.compose.material3.DockedSearchBar
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SearchBar
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.testutils.LayeredComposeTestCase
+import androidx.compose.testutils.ToggleableTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class SearchBarBenchmark(private val type: SearchBarType) {
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun parameters() = SearchBarType.values()
+    }
+
+    @get:Rule
+    val benchmarkRule = ComposeBenchmarkRule()
+
+    private val testCaseFactory = { SearchBarTestCase(type) }
+
+    @Test
+    fun firstPixel() {
+        benchmarkRule.benchmarkFirstRenderUntilStable(testCaseFactory)
+    }
+
+    @Test
+    fun changeActiveState() {
+        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+            caseFactory = testCaseFactory,
+            assertOneRecomposition = false,
+        )
+    }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+internal class SearchBarTestCase(
+    private val type: SearchBarType
+) : LayeredComposeTestCase(), ToggleableTestCase {
+    private lateinit var state: MutableState<Boolean>
+
+    @Composable
+    override fun MeasuredContent() {
+        state = remember { mutableStateOf(true) }
+
+        when (type) {
+            SearchBarType.FullScreen ->
+                SearchBar(
+                    query = "",
+                    onQueryChange = {},
+                    onSearch = {},
+                    active = state.value,
+                    onActiveChange = { state.value = it },
+                    content = {},
+                )
+            SearchBarType.Docked ->
+                DockedSearchBar(
+                    query = "",
+                    onQueryChange = {},
+                    onSearch = {},
+                    active = state.value,
+                    onActiveChange = { state.value = it },
+                    content = {},
+                )
+        }
+    }
+
+    @Composable
+    override fun ContentWrappers(content: @Composable () -> Unit) {
+        MaterialTheme {
+            content()
+        }
+    }
+
+    override fun toggleState() {
+        state.value = !state.value
+    }
+}
+
+enum class SearchBarType {
+    FullScreen, Docked
+}
diff --git a/compose/material3/material3-adaptive-navigation-suite/build.gradle b/compose/material3/material3-adaptive-navigation-suite/build.gradle
index a59893c..72aee4b 100644
--- a/compose/material3/material3-adaptive-navigation-suite/build.gradle
+++ b/compose/material3/material3-adaptive-navigation-suite/build.gradle
@@ -66,7 +66,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api("androidx.annotation:annotation:1.1.0")
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
             }
         }
 
diff --git a/compose/material3/material3-adaptive/build.gradle b/compose/material3/material3-adaptive/build.gradle
index 385ccc3..201ef13 100644
--- a/compose/material3/material3-adaptive/build.gradle
+++ b/compose/material3/material3-adaptive/build.gradle
@@ -65,7 +65,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api("androidx.annotation:annotation:1.1.0")
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
                 implementation("androidx.window:window:1.2.0")
             }
         }
diff --git a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.android.kt b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.android.kt
index 141ca31..4e02b85 100644
--- a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.android.kt
+++ b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.android.kt
@@ -19,13 +19,13 @@
 import androidx.compose.runtime.Stable
 
 /**
- * The state of [ListDetailPaneScaffold]. It provides the layout directive and value state that will
+ * The state of [ThreePaneScaffold]. It provides the layout directive and value state that will
  * be updated directly. It also provides functions to perform navigation.
  *
  * @property scaffoldDirective the current layout directives that the associated
- *           [ListDetailPaneScaffold] needs to follow. It's supposed to be automatically updated
+ *           [ThreePaneScaffold] needs to follow. It's supposed to be automatically updated
  *           when the window configuration changes.
- * @property scaffoldValue the current layout value of the associated [ListDetailPaneScaffold],
+ * @property scaffoldValue the current layout value of the associated [ThreePaneScaffold],
  *           which represents unique layout states of the scaffold.
  */
 @ExperimentalMaterial3AdaptiveApi
diff --git a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldNavigator.android.kt b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldNavigator.android.kt
index 49f8e40..2d8968d 100644
--- a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldNavigator.android.kt
+++ b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldNavigator.android.kt
@@ -81,7 +81,9 @@
     fun canNavigateBack(scaffoldValueMustChange: Boolean = true): Boolean
 
     /**
-     * Navigates to the previous destination.
+     * Navigates to the previous destination. Returns `true` if there is a previous destination to
+     * navigate back to. When implementing this function, please make sure the logic is consistent
+     * with [canNavigateBack].
      *
      * @param popUntilScaffoldValueChange `true` if we should skip all backstack entries without any
      *        scaffold value changes. This may happen in a multi-pane scenario that both the current
diff --git a/compose/material3/material3-window-size-class/build.gradle b/compose/material3/material3-window-size-class/build.gradle
index 1308a54..ed1e25f 100644
--- a/compose/material3/material3-window-size-class/build.gradle
+++ b/compose/material3/material3-window-size-class/build.gradle
@@ -40,10 +40,10 @@
         commonMain {
             dependencies {
                 implementation(libs.kotlinStdlibCommon)
-                implementation("androidx.compose.ui:ui-util:1.6.0-rc01")
-                api("androidx.compose.runtime:runtime:1.6.0-rc01")
-                api("androidx.compose.ui:ui:1.6.0-rc01")
-                api("androidx.compose.ui:ui-unit:1.6.0-rc01")
+                implementation(project(":compose:ui:ui-util"))
+                api(project(":compose:runtime:runtime"))
+                api(project(":compose:ui:ui"))
+                api(project(":compose:ui:ui-unit"))
             }
         }
 
@@ -74,7 +74,7 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
                 implementation("androidx.window:window:1.0.0")
             }
         }
diff --git a/compose/material3/material3/build.gradle b/compose/material3/material3/build.gradle
index 8db81f3..b80bcde 100644
--- a/compose/material3/material3/build.gradle
+++ b/compose/material3/material3/build.gradle
@@ -41,18 +41,17 @@
         commonMain {
             dependencies {
                 implementation(libs.kotlinStdlibCommon)
-                implementation("androidx.collection:collection:1.4.0-rc01")
-                implementation("androidx.compose.animation:animation-core:1.6.0-rc01")
+                implementation(project(":collection:collection"))
+                implementation(project(":compose:animation:animation-core"))
 
-                api("androidx.compose.foundation:foundation:1.6.0-rc01")
-                api("androidx.compose.foundation:foundation-layout:1.6.0-rc01")
-                api("androidx.compose.material:material-icons-core:1.6.0-rc01")
-                api("androidx.compose.material:material-ripple:1.6.0-rc01")
-                api("androidx.compose.runtime:runtime:1.6.0-rc01")
-                api("androidx.compose.ui:ui-graphics:1.6.0-rc01")
-                api("androidx.compose.ui:ui-text:1.6.0-rc01")
-
-                implementation("androidx.compose.ui:ui-util:1.6.0-rc01")
+                api(project(":compose:foundation:foundation"))
+                api(project(":compose:foundation:foundation-layout"))
+                api(project(":compose:material:material-icons-core"))
+                api(project(":compose:material:material-ripple"))
+                api(project(":compose:runtime:runtime"))
+                api(project(":compose:ui:ui"))
+                api(project(":compose:ui:ui-text"))
+                implementation(project(":compose:ui:ui-util"))
             }
         }
 
@@ -70,14 +69,14 @@
         skikoMain {
             dependsOn(commonMain)
             dependencies {
-                api("androidx.compose.animation:animation-core:1.6.0-rc01")
-                api("androidx.compose.runtime:runtime:1.6.0-rc01")
-                api("androidx.compose.ui:ui:1.6.0-rc01")
-                api("androidx.compose.ui:ui-text:1.6.0-rc01")
-                api("androidx.compose.foundation:foundation-layout:1.6.0-rc01")
+                api(project(":compose:animation:animation-core"))
+                api(project(":compose:runtime:runtime"))
+                api(project(":compose:ui:ui"))
+                api(project(":compose:ui:ui-text"))
+                api(project(":compose:foundation:foundation-layout"))
 
-                implementation("androidx.compose.animation:animation:1.6.0-rc01")
-                implementation("androidx.compose.ui:ui-util:1.6.0-rc01")
+                implementation(project(":compose:animation:animation"))
+                implementation(project(":compose:ui:ui-util"))
             }
         }
 
@@ -85,7 +84,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api("androidx.annotation:annotation:1.1.0")
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
                 implementation("androidx.activity:activity-compose:1.5.0")
 
                 // TODO: remove next 3 dependencies when b/202810604 is fixed
@@ -168,4 +167,4 @@
     sourceSets.androidTest.assets.srcDirs +=
             project.rootDir.absolutePath + "/../../golden/compose/material3/material3"
     namespace "androidx.compose.material3"
-}
+}
\ No newline at end of file
diff --git a/compose/material3/material3/integration-tests/material3-catalog/build.gradle b/compose/material3/material3/integration-tests/material3-catalog/build.gradle
index c3d256e1..36df912 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/build.gradle
+++ b/compose/material3/material3/integration-tests/material3-catalog/build.gradle
@@ -37,7 +37,7 @@
     implementation project(":compose:runtime:runtime")
     implementation project(":compose:foundation:foundation-layout")
     implementation project(":compose:ui:ui")
-    implementation("androidx.compose.material:material:1.6.0-beta01")
+    implementation project(":compose:material:material")
     implementation project(":compose:material:material-icons-extended")
     implementation project(":compose:material3:material3")
     implementation project(":compose:material3:material3:material3-samples")
diff --git a/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/Material3Demos.kt b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/Material3Demos.kt
index 4218015..1fdbf6a 100644
--- a/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/Material3Demos.kt
+++ b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/Material3Demos.kt
@@ -26,6 +26,7 @@
         ComposableDemo("Pull To Refresh") { PullToRefreshDemo() },
         ComposableDemo("Shape") { ShapeDemo() },
         ComposableDemo("Swipe To Dismiss") { SwipeToDismissDemo() },
-        ComposableDemo("Tooltip") { TooltipDemo() }
+        ComposableDemo("Tooltip") { TooltipDemo() },
+        ComposableDemo("Text fields") { MaterialTextFieldDemo() },
     ),
 )
diff --git a/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/TextFieldDemos.kt b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/TextFieldDemos.kt
new file mode 100644
index 0000000..2cf9db4
--- /dev/null
+++ b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/TextFieldDemos.kt
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.demos
+
+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.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredWidth
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.Info
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.RadioButton
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun MaterialTextFieldDemo() {
+    Column(
+        Modifier
+            .verticalScroll(rememberScrollState())
+            .padding(PaddingValues(10.dp))) {
+        var text by rememberSaveable { mutableStateOf("") }
+        var leadingChecked by rememberSaveable { mutableStateOf(false) }
+        var trailingChecked by rememberSaveable { mutableStateOf(false) }
+        val characterCounterChecked by rememberSaveable { mutableStateOf(false) }
+        var singleLineChecked by rememberSaveable { mutableStateOf(true) }
+        var selectedOption by rememberSaveable { mutableStateOf(Option.None) }
+        var selectedTextField by rememberSaveable { mutableStateOf(TextFieldType.Filled) }
+        var disabled by rememberSaveable { mutableStateOf(false) }
+        var readOnly by rememberSaveable { mutableStateOf(false) }
+
+        val textField: @Composable () -> Unit = @Composable {
+            when (selectedTextField) {
+                TextFieldType.Filled ->
+                    TextField(
+                        value = text,
+                        onValueChange = { text = it },
+                        enabled = !disabled,
+                        readOnly = readOnly,
+                        singleLine = singleLineChecked,
+                        label = {
+                            val label =
+                                "Label" + if (selectedOption == Option.Error) "*" else ""
+                            Text(text = label)
+                        },
+                        leadingIcon = if (leadingChecked) {
+                            @Composable { Icon(Icons.Filled.Favorite, "Favorite") }
+                        } else {
+                            null
+                        },
+                        trailingIcon = if (trailingChecked) {
+                            @Composable { Icon(Icons.Filled.Info, "Info") }
+                        } else {
+                            null
+                        },
+                        isError = selectedOption == Option.Error,
+                        modifier = Modifier.requiredWidth(300.dp)
+                    )
+
+                TextFieldType.Outlined ->
+                    OutlinedTextField(
+                        value = text,
+                        onValueChange = { text = it },
+                        enabled = !disabled,
+                        readOnly = readOnly,
+                        singleLine = singleLineChecked,
+                        label = {
+                            val label =
+                                "Label" + if (selectedOption == Option.Error) "*" else ""
+                            Text(text = label)
+                        },
+                        leadingIcon = if (leadingChecked) {
+                            @Composable { Icon(Icons.Filled.Favorite, "Favorite") }
+                        } else {
+                            null
+                        },
+                        trailingIcon = if (trailingChecked) {
+                            @Composable { Icon(Icons.Filled.Info, "Info") }
+                        } else {
+                            null
+                        },
+                        isError = selectedOption == Option.Error,
+                        modifier = Modifier.requiredWidth(300.dp)
+                    )
+            }
+        }
+
+        Box(
+            Modifier
+                .height(150.dp)
+                .align(Alignment.CenterHorizontally)) {
+            if (selectedOption == Option.None) {
+                textField()
+            } else {
+                TextFieldWithMessage(selectedOption, textField)
+            }
+        }
+
+        Column {
+            Title("Text field type")
+            TextFieldType.values().map { it.name }.forEach { textType ->
+                Row(
+                    Modifier
+                        .fillMaxWidth()
+                        .selectable(
+                            selected = (textType == selectedTextField.name),
+                            onClick = {
+                                selectedTextField = TextFieldType.valueOf(textType)
+                            }
+                        )
+                        .padding(horizontal = 16.dp)
+                ) {
+                    RadioButton(
+                        selected = (textType == selectedTextField.name),
+                        onClick = null
+                    )
+                    Text(
+                        text = textType,
+                        style = MaterialTheme.typography.bodyLarge.merge(),
+                        modifier = Modifier.padding(start = 16.dp)
+                    )
+                }
+            }
+
+            Title("Options")
+            OptionRow(
+                title = "Leading icon",
+                checked = leadingChecked,
+                onCheckedChange = { leadingChecked = it }
+            )
+            OptionRow(
+                title = "Trailing icon",
+                checked = trailingChecked,
+                onCheckedChange = { trailingChecked = it }
+            )
+            OptionRow(
+                title = "Single line",
+                checked = singleLineChecked,
+                onCheckedChange = { singleLineChecked = it }
+            )
+            OptionRow(
+                title = "Character counter (TODO)",
+                checked = characterCounterChecked,
+                enabled = false,
+                onCheckedChange = { /* TODO */ }
+            )
+
+            Spacer(Modifier.height(20.dp))
+
+            Title("Assistive text")
+            Option.values().map { it.name }.forEach { text ->
+                Row(
+                    Modifier
+                        .fillMaxWidth()
+                        .selectable(
+                            selected = (text == selectedOption.name),
+                            onClick = { selectedOption = Option.valueOf(text) }
+                        )
+                        .padding(horizontal = 16.dp)
+                ) {
+                    RadioButton(
+                        selected = (text == selectedOption.name),
+                        onClick = null
+                    )
+                    Text(
+                        text = text,
+                        style = MaterialTheme.typography.bodyLarge.merge(),
+                        modifier = Modifier.padding(start = 16.dp)
+                    )
+                }
+            }
+
+            Title("Other settings")
+            OptionRow(
+                title = "Read-only",
+                checked = readOnly,
+                onCheckedChange = { readOnly = it }
+            )
+            OptionRow(
+                title = "Disabled",
+                checked = disabled,
+                onCheckedChange = { disabled = it }
+            )
+        }
+    }
+}
+
+/**
+ * Text field with helper or error message below.
+ */
+@Composable
+private fun TextFieldWithMessage(
+    helperMessageOption: Option,
+    content: @Composable () -> Unit
+) {
+    val typography = MaterialTheme.typography.labelMedium
+    val color = when (helperMessageOption) {
+        Option.Helper -> {
+            MaterialTheme.colorScheme.onSurface
+        }
+
+        Option.Error -> MaterialTheme.colorScheme.error
+        else -> Color.Unspecified
+    }
+
+    Column {
+        Box(modifier = Modifier.weight(1f, fill = false)) { content() }
+        Text(
+            text = "Helper message",
+            color = color,
+            style = typography,
+            modifier = Modifier.padding(start = 16.dp)
+        )
+    }
+}
+
+@Composable
+private fun ColumnScope.Title(title: String) {
+    Text(
+        text = title,
+        style = MaterialTheme.typography.bodyLarge,
+        modifier = Modifier.align(Alignment.CenterHorizontally)
+    )
+    Spacer(Modifier.height(10.dp))
+}
+
+@Composable
+private fun OptionRow(
+    title: String,
+    checked: Boolean,
+    onCheckedChange: (Boolean) -> Unit,
+    enabled: Boolean = true
+) {
+    Row(
+        Modifier
+            .padding(start = 10.dp, top = 10.dp)
+            .fillMaxWidth()
+            .toggleable(
+                value = checked, onValueChange = onCheckedChange, enabled = enabled
+            )
+    ) {
+        Checkbox(checked = checked, onCheckedChange = null, enabled = enabled)
+        Spacer(Modifier.width(20.dp))
+        Text(text = title, style = MaterialTheme.typography.bodyLarge)
+    }
+}
+
+/**
+ * Helper message option
+ */
+private enum class Option { None, Helper, Error }
+
+private enum class TextFieldType { Filled, Outlined }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
index ab0b33b..0af697f 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-@file:Suppress("DEPRECATION")
-
 package androidx.compose.material3
 
 import android.os.Build
+import android.text.InputType.TYPE_CLASS_TEXT
+import android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.EditorInfo.IME_ACTION_GO
 import androidx.compose.foundation.background
 import androidx.compose.foundation.horizontalScroll
 import androidx.compose.foundation.layout.Box
@@ -50,12 +53,15 @@
 import androidx.compose.ui.node.Ref
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalFocusManager
-import androidx.compose.ui.platform.LocalTextInputService
+import androidx.compose.ui.platform.PlatformTextInputMethodRequest
+import androidx.compose.ui.platform.PlatformTextInputSession
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.semantics.error
 import androidx.compose.ui.semantics.getOrNull
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.PlatformTextInputMethodTestOverride
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertIsDisplayed
@@ -72,11 +78,8 @@
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.ImeOptions
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.text.input.PlatformTextInputService
-import androidx.compose.ui.text.input.TextInputService
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
@@ -85,16 +88,13 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import com.google.common.truth.Truth.assertThat
 import kotlin.math.roundToInt
+import kotlinx.coroutines.awaitCancellation
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.any
-import org.mockito.kotlin.atLeastOnce
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.verify
 
 @OptIn(ExperimentalMaterial3Api::class)
 @MediumTest
@@ -1312,15 +1312,26 @@
         }
     }
 
+    @OptIn(ExperimentalTestApi::class)
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
     fun testOutlinedTextField_imeActionAndKeyboardTypePropagatedDownstream() {
-        val platformTextInputService = mock<PlatformTextInputService>()
-        val textInputService = TextInputService(platformTextInputService)
+        var editorInfo: EditorInfo? = null
+        val sessionHandler = object : PlatformTextInputSession {
+            override val view: View = View(getInstrumentation().targetContext)
+
+            override suspend fun startInputMethod(
+                request: PlatformTextInputMethodRequest
+            ): Nothing {
+                EditorInfo().also {
+                    request.createInputConnection(it)
+                    editorInfo = it
+                }
+                awaitCancellation()
+            }
+        }
         rule.setContent {
-            CompositionLocalProvider(
-                LocalTextInputService provides textInputService
-            ) {
+            PlatformTextInputMethodTestOverride(sessionHandler) {
                 val text = remember { mutableStateOf("") }
                 OutlinedTextField(
                     modifier = Modifier.testTag(TextFieldTag),
@@ -1337,17 +1348,12 @@
         rule.onNodeWithTag(TextFieldTag).performClick()
 
         rule.runOnIdle {
-            verify(platformTextInputService, atLeastOnce()).startInput(
-                value = any(),
-                imeOptions = eq(
-                    ImeOptions(
-                        keyboardType = KeyboardType.Email,
-                        imeAction = ImeAction.Go
-                    )
-                ),
-                onEditCommand = any(),
-                onImeActionPerformed = any()
-            )
+            @Suppress("NAME_SHADOWING")
+            val editorInfo = editorInfo ?: throw AssertionError("Input session never started")
+            val expectedOptions = IME_ACTION_GO
+            val expectedInputType = TYPE_CLASS_TEXT or TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+            assertThat(editorInfo.imeOptions and expectedOptions).isEqualTo(expectedOptions)
+            assertThat(editorInfo.inputType and expectedInputType).isEqualTo(expectedInputType)
         }
     }
 
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt
index 8fb12a6..a9fec36 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt
@@ -21,6 +21,7 @@
 import android.content.Context
 import android.os.Build
 import android.view.View
+import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputMethodManager
 import androidx.compose.foundation.background
 import androidx.compose.foundation.horizontalScroll
@@ -61,14 +62,17 @@
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.LocalSoftwareKeyboardController
-import androidx.compose.ui.platform.LocalTextInputService
 import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.platform.PlatformTextInputMethodRequest
+import androidx.compose.ui.platform.PlatformTextInputSession
 import androidx.compose.ui.platform.SoftwareKeyboardController
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.semantics.error
 import androidx.compose.ui.semantics.getOrNull
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.PlatformTextInputMethodTestOverride
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHeightIsEqualTo
@@ -87,12 +91,9 @@
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.ImeOptions
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.OffsetMapping
 import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.text.input.PlatformTextInputService
-import androidx.compose.ui.text.input.TextInputService
 import androidx.compose.ui.text.input.TransformedText
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.Density
@@ -103,18 +104,15 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
 import com.google.common.truth.Truth.assertThat
 import kotlin.math.roundToInt
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.launch
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.any
-import org.mockito.kotlin.atLeastOnce
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.verify
 
 @OptIn(ExperimentalMaterial3Api::class)
 @MediumTest
@@ -1278,17 +1276,29 @@
         }
     }
 
+    @OptIn(ExperimentalTestApi::class)
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
     fun testTextField_imeActionAndKeyboardTypePropagatedDownstream() {
-        val platformTextInputService = mock<PlatformTextInputService>()
-        val textInputService = TextInputService(platformTextInputService)
+        var editorInfo: EditorInfo? = null
+        val sessionHandler = object : PlatformTextInputSession {
+            override val view: View =
+                View(InstrumentationRegistry.getInstrumentation().targetContext)
+
+            override suspend fun startInputMethod(
+                request: PlatformTextInputMethodRequest
+            ): Nothing {
+                EditorInfo().also {
+                    request.createInputConnection(it)
+                    editorInfo = it
+                }
+                awaitCancellation()
+            }
+        }
         rule.setContent {
-            CompositionLocalProvider(
-                LocalTextInputService provides textInputService
-            ) {
+            PlatformTextInputMethodTestOverride(sessionHandler) {
                 val text = remember { mutableStateOf("") }
-                TextField(
+                OutlinedTextField(
                     modifier = Modifier.testTag(TextFieldTag),
                     value = text.value,
                     onValueChange = { text.value = it },
@@ -1303,17 +1313,13 @@
         rule.onNodeWithTag(TextFieldTag).performClick()
 
         rule.runOnIdle {
-            verify(platformTextInputService, atLeastOnce()).startInput(
-                value = any(),
-                imeOptions = eq(
-                    ImeOptions(
-                        keyboardType = KeyboardType.Email,
-                        imeAction = ImeAction.Go
-                    )
-                ),
-                onEditCommand = any(),
-                onImeActionPerformed = any()
-            )
+            @Suppress("NAME_SHADOWING")
+            val editorInfo = editorInfo ?: throw AssertionError("Input session never started")
+            val expectedOptions = EditorInfo.IME_ACTION_GO
+            val expectedInputType = EditorInfo.TYPE_CLASS_TEXT or
+                EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+            assertThat(editorInfo.imeOptions and expectedOptions).isEqualTo(expectedOptions)
+            assertThat(editorInfo.inputType and expectedInputType).isEqualTo(expectedInputType)
         }
     }
 
diff --git a/compose/material3/material3/src/androidUnitTest/kotlin/androidx/compose/material3/carousel/KeylineTest.kt b/compose/material3/material3/src/androidUnitTest/kotlin/androidx/compose/material3/carousel/KeylineTest.kt
index e528318..12369ad 100644
--- a/compose/material3/material3/src/androidUnitTest/kotlin/androidx/compose/material3/carousel/KeylineTest.kt
+++ b/compose/material3/material3/src/androidUnitTest/kotlin/androidx/compose/material3/carousel/KeylineTest.kt
@@ -92,6 +92,46 @@
         assertThat(keylineList.getKeylineAfter(Float.MAX_VALUE)).isEqualTo(keylineList.last())
     }
 
+    @Test
+    fun testKeylineListLerp() {
+        val carouselMainAxisSize = StrategyTest.large + StrategyTest.medium + StrategyTest.small
+        val from = keylineListOf(carouselMainAxisSize, 1, StrategyTest.large / 2) {
+            add(StrategyTest.xSmall, isAnchor = true)
+            add(StrategyTest.large)
+            add(StrategyTest.medium)
+            add(StrategyTest.small)
+            add(StrategyTest.xSmall, isAnchor = true)
+        }
+        val to = keylineListOf(
+            carouselMainAxisSize,
+            2,
+            StrategyTest.small + (StrategyTest.large / 2)
+        ) {
+            add(StrategyTest.xSmall, isAnchor = true)
+            add(StrategyTest.small)
+            add(StrategyTest.large)
+            add(StrategyTest.medium)
+            add(StrategyTest.xSmall, isAnchor = true)
+        }
+
+        // Create the expected interpolated KeylineList by using the KeylineList class' constructor
+        // directly. Otherwise, keylineListOf will set offsets and unadjusted offsets based on the
+        // pivot offset and will differ than the directly interpolated output of lerp.
+        val half = KeylineList(
+            listOf(
+                Keyline(StrategyTest.xSmall, -2.5f, -90f, false, true, false, 0f),
+                Keyline(60f, 30f, 10f, false, false, false, 0f),
+                Keyline(80f, 100f, 110f, true, false, true, 0f),
+                Keyline(40f, 160f, 210f, false, false, false, 0f),
+                Keyline(StrategyTest.xSmall, 182.5f, 310f, false, true, false, 0f)
+            )
+        )
+
+        assertThat(lerp(from, to, 0f)).isEqualTo(from)
+        assertThat(lerp(from, to, 1f)).isEqualTo(to)
+        assertThat(lerp(from, to, .5f)).isEqualTo(half)
+    }
+
     companion object {
         private const val LargeSize = 100f
         private const val SmallSize = 20f
diff --git a/compose/material3/material3/src/androidUnitTest/kotlin/androidx/compose/material3/carousel/StrategyTest.kt b/compose/material3/material3/src/androidUnitTest/kotlin/androidx/compose/material3/carousel/StrategyTest.kt
index 27998ae..c2acd24 100644
--- a/compose/material3/material3/src/androidUnitTest/kotlin/androidx/compose/material3/carousel/StrategyTest.kt
+++ b/compose/material3/material3/src/androidUnitTest/kotlin/androidx/compose/material3/carousel/StrategyTest.kt
@@ -266,42 +266,6 @@
             .isEqualTo(endSteps[2])
     }
 
-    @Test
-    fun testKeylineListLerp() {
-        val carouselMainAxisSize = large + medium + small
-        val from = keylineListOf(carouselMainAxisSize, 1, large / 2) {
-            add(xSmall, isAnchor = true)
-            add(large)
-            add(medium)
-            add(small)
-            add(xSmall, isAnchor = true)
-        }
-        val to = keylineListOf(carouselMainAxisSize, 2, small + (large / 2)) {
-            add(xSmall, isAnchor = true)
-            add(small)
-            add(large)
-            add(medium)
-            add(xSmall, isAnchor = true)
-        }
-
-        // Create the expected interpolated KeylineList by using the KeylineList class' constructor
-        // directly. Otherwise, keylineListOf will set offsets and unadjusted offsets based on the
-        // pivot offset and will differ than the directly interpolated output of lerp.
-        val half = KeylineList(
-            listOf(
-                Keyline(xSmall, -2.5f, -90f, false, true, false, 0f),
-                Keyline(60f, 30f, 10f, false, false, false, 0f),
-                Keyline(80f, 100f, 110f, true, false, true, 0f),
-                Keyline(40f, 160f, 210f, false, false, false, 0f),
-                Keyline(xSmall, 182.5f, 310f, false, true, false, 0f)
-            )
-        )
-
-        assertThat(lerp(from, to, 0f)).isEqualTo(from)
-        assertThat(lerp(from, to, 1f)).isEqualTo(to)
-        assertThat(lerp(from, to, .5f)).isEqualTo(half)
-    }
-
     private fun assertEqualWithFloatTolerance(
         tolerance: Float,
         actual: Keyline,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Carousel.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Carousel.kt
new file mode 100644
index 0000000..8500938
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Carousel.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.carousel
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.ShapeDefaults
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.RoundRect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import kotlin.math.roundToInt
+
+/**
+ * A enumeration of ways items can be aligned along a carousel's main axis
+ */
+internal enum class CarouselAlignment {
+    /** Start aligned carousels place focal items at the start/top of the container */
+    Start,
+    /** Center aligned carousels place focal items in the middle of the container */
+    Center,
+    /** End aligned carousels place focal items at the end/bottom of the container */
+    End
+}
+
+/**
+ * A modifier that handles clipping and translating an item as it moves along the scrolling axis
+ * of a Carousel.
+ *
+ * @param index the index of the item in the carousel
+ * @param viewportSize the size of the carousel container
+ * @param orientation the orientation of the carousel
+ * @param itemsCount the total number of items in the carousel
+ * @param scrollOffset the amount the carousel has been scrolled in pixels
+ * @param strategy the strategy used to mask and translate items in the carousel
+ */
+internal fun Modifier.carouselItem(
+    index: Int,
+    itemsCount: Int,
+    viewportSize: IntSize,
+    orientation: Orientation,
+    scrollOffset: Float,
+    strategy: Strategy
+): Modifier {
+    val isVertical = orientation == Orientation.Vertical
+    val mainAxisCarouselSize = if (isVertical) viewportSize.height else viewportSize.width
+    if (mainAxisCarouselSize == 0) {
+        return this
+    }
+    val maxScrollOffset =
+        itemsCount * strategy.itemMainAxisSize - mainAxisCarouselSize
+    val keylines = strategy.getKeylineListForScrollOffset(scrollOffset, maxScrollOffset)
+
+    // Find center of the item at this index
+    val unadjustedCenter =
+        (index * strategy.itemMainAxisSize) + (strategy.itemMainAxisSize / 2f) - scrollOffset
+
+    // Find the keyline before and after this item's center and create an interpolated
+    // keyline that the item should use for its clip shape and offset
+    val keylineBefore =
+        keylines.getKeylineBefore(unadjustedCenter)
+    val keylineAfter =
+        keylines.getKeylineAfter(unadjustedCenter)
+    val progress = getProgress(keylineBefore, keylineAfter, unadjustedCenter)
+    val interpolatedKeyline = lerp(keylineBefore, keylineAfter, progress)
+    val isOutOfKeylineBounds = keylineBefore == keylineAfter
+
+    return this then layout { measurable, constraints ->
+        // Force the item to use the strategy's itemMainAxisSize along its main axis
+        val mainAxisSize = strategy.itemMainAxisSize
+        val itemConstraints = if (isVertical) {
+            constraints.copy(
+                minWidth = constraints.minWidth,
+                maxWidth = constraints.maxWidth,
+                minHeight = mainAxisSize.roundToInt(),
+                maxHeight = mainAxisSize.roundToInt()
+            )
+        } else {
+            constraints.copy(
+                minWidth = mainAxisSize.roundToInt(),
+                maxWidth = mainAxisSize.roundToInt(),
+                minHeight = constraints.minHeight,
+                maxHeight = constraints.maxHeight
+            )
+        }
+
+        val placeable = measurable.measure(itemConstraints)
+        layout(placeable.width, placeable.height) {
+            placeable.place(0, 0)
+        }
+    } then graphicsLayer {
+        // Clip the item
+        clip = true
+        shape = object : Shape {
+            // TODO: Find a way to use the shape of the item set by the client for each item
+            val roundedCornerShape = RoundedCornerShape(ShapeDefaults.ExtraLarge.topStart)
+            override fun createOutline(
+                size: Size,
+                layoutDirection: LayoutDirection,
+                density: Density
+            ): Outline {
+                val centerX =
+                    if (isVertical) size.height / 2f else strategy.itemMainAxisSize / 2f
+                val centerY =
+                    if (isVertical) strategy.itemMainAxisSize / 2f else size.height / 2f
+                val halfMaskWidth =
+                    if (isVertical) size.width / 2f else interpolatedKeyline.size / 2f
+                val halfMaskHeight =
+                    if (isVertical) interpolatedKeyline.size / 2f else size.height / 2f
+                val rect = Rect(
+                    left = centerX - halfMaskWidth,
+                    top = centerY - halfMaskHeight,
+                    right = centerX + halfMaskWidth,
+                    bottom = centerY + halfMaskHeight
+                )
+                val cornerSize =
+                    roundedCornerShape.topStart.toPx(
+                        Size(rect.width, rect.height),
+                        density
+                    )
+                val cornerRadius = CornerRadius(cornerSize)
+                return Outline.Rounded(
+                    RoundRect(
+                        rect = rect,
+                        topLeft = cornerRadius,
+                        topRight = cornerRadius,
+                        bottomRight = cornerRadius,
+                        bottomLeft = cornerRadius
+                    )
+                )
+            }
+        }
+
+        // After clipping, the items will have white space between them. Translate the items to
+        // pin their edges together
+        var translation = interpolatedKeyline.offset - unadjustedCenter
+        if (isOutOfKeylineBounds) {
+            // If this item is beyond the first or last keyline, continue to offset the item
+            // by cutting its unadjustedOffset according to its masked size.
+            val outOfBoundsOffset = (unadjustedCenter - interpolatedKeyline.unadjustedOffset) /
+                interpolatedKeyline.size
+            translation += outOfBoundsOffset
+        }
+        if (isVertical) {
+            translationY = translation
+        } else {
+            translationX = translation
+        }
+    }
+}
+
+/**
+ * Returns a float between 0 and 1 that represents how far [unadjustedOffset] is between
+ * [before] and [after].
+ *
+ * @param before the first keyline whose unadjustedOffset is less than [unadjustedOffset]
+ * @param after the first keyline whose unadjustedOffset is greater than [unadjustedOffset]
+ * @param unadjustedOffset the unadjustedOffset between [before] and [after]'s unadjustedOffset that
+ * a progress value will be returned for
+ */
+private fun getProgress(before: Keyline, after: Keyline, unadjustedOffset: Float): Float {
+    if (before == after) {
+        return 1f
+    }
+
+    val total = after.unadjustedOffset - before.unadjustedOffset
+    return (unadjustedOffset - before.unadjustedOffset) / total
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Keyline.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Keyline.kt
index 472a0eb..63c612e 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Keyline.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Keyline.kt
@@ -17,6 +17,7 @@
 package androidx.compose.material3.carousel
 
 import androidx.compose.ui.util.fastFirstOrNull
+import androidx.compose.ui.util.fastMapIndexed
 
 /**
  * A structure that is fixed at a specific [offset] along a scrolling axis and
@@ -202,10 +203,6 @@
     }
 }
 
-internal enum class CarouselAlignment {
-    Start, Center, End
-}
-
 /**
  * Returns a [KeylineList] by aligning the focal range relative to the carousel container.
  */
@@ -468,3 +465,41 @@
             offset + (size / 2) > carouselMainAxisSize
     }
 }
+
+/**
+ * Returns an interpolated [Keyline] whose values are all interpolated based on [fraction]
+ * between the [start] and [end] keylines.
+ */
+internal fun lerp(start: Keyline, end: Keyline, fraction: Float): Keyline {
+    return Keyline(
+        size = androidx.compose.ui.util.lerp(start.size, end.size, fraction),
+        offset = androidx.compose.ui.util.lerp(start.offset, end.offset, fraction),
+        unadjustedOffset = androidx.compose.ui.util.lerp(
+            start.unadjustedOffset,
+            end.unadjustedOffset,
+            fraction
+        ),
+        isFocal = if (fraction < .5f) start.isFocal else end.isFocal,
+        isAnchor = if (fraction < .5f) start.isAnchor else end.isAnchor,
+        isPivot = if (fraction < .5f) start.isPivot else end.isPivot,
+        cutoff = androidx.compose.ui.util.lerp(start.cutoff, end.cutoff, fraction)
+    )
+}
+
+/**
+ * Returns an interpolated KeylineList between [from] and [to].
+ *
+ * Unlike creating a [KeylineList] using [keylineListOf], this method does not set unadjusted
+ * offsets by calculating them from a pivot index. This method simply interpolates all values of
+ * all keylines between the given pair.
+ */
+internal fun lerp(
+    from: KeylineList,
+    to: KeylineList,
+    fraction: Float
+): KeylineList {
+    val interpolatedKeylines = from.fastMapIndexed { i, k ->
+        lerp(k, to[i], fraction)
+    }
+    return KeylineList(interpolatedKeylines)
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Strategy.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Strategy.kt
index 269d0c5..4febab9 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Strategy.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Strategy.kt
@@ -16,12 +16,10 @@
 
 package androidx.compose.material3.carousel
 
-import androidx.annotation.VisibleForTesting
 import androidx.collection.FloatList
 import androidx.collection.mutableFloatListOf
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastMapIndexed
 import androidx.compose.ui.util.lerp
 import kotlin.math.roundToInt
 
@@ -86,6 +84,11 @@
 ) {
 
     /**
+     * The size of items when in focus and fully unmasked.
+     */
+    internal val itemMainAxisSize = defaultKeylines.firstFocal.size
+
+    /**
      * Returns the [KeylineList] that should be used for the current [scrollOffset].
      *
      * @param scrollOffset the current scroll offset of the scrollable component
@@ -163,7 +166,7 @@
          * @param carouselMainAxisSize the size of the carousel container in scrolling axis
          * @param keylineList the default keylines that will be used to create the strategy
          */
-        internal fun create(
+        fun create(
             /** The size of the carousel in the main axis. */
             carouselMainAxisSize: Float,
             /** The keylines along the main axis */
@@ -455,42 +458,6 @@
     }
 }
 
-/**
- * Returns an interpolated [Keyline] whose values are all interpolated based on [fraction]
- * between the [start] and [end] keylines.
- */
-@VisibleForTesting
-internal fun lerp(start: Keyline, end: Keyline, fraction: Float): Keyline {
-    return Keyline(
-        size = lerp(start.size, end.size, fraction),
-        offset = lerp(start.offset, end.offset, fraction),
-        unadjustedOffset = lerp(start.unadjustedOffset, end.unadjustedOffset, fraction),
-        isFocal = if (fraction < .5f) start.isFocal else end.isFocal,
-        isAnchor = if (fraction < .5f) start.isAnchor else end.isAnchor,
-        isPivot = if (fraction < .5f) start.isPivot else end.isPivot,
-        cutoff = lerp(start.cutoff, end.cutoff, fraction)
-    )
-}
-
-/**
- * Returns an interpolated KeylineList between [from] and [to].
- *
- * Unlike creating a [KeylineList] using [keylineListOf], this method does not set unadjusted
- * offsets by calculating them from a pivot index. This method simply interpolates all values of
- * all keylines between the given pair.
- */
-@VisibleForTesting
-internal fun lerp(
-    from: KeylineList,
-    to: KeylineList,
-    fraction: Float
-): KeylineList {
-    val interpolatedKeylines = from.fastMapIndexed { i, k ->
-        lerp(k, to[i], fraction)
-    }
-    return KeylineList(interpolatedKeylines)
-}
-
 private fun lerp(
     outputMin: Float,
     outputMax: Float,
diff --git a/compose/runtime/design/how-compose-works.md b/compose/runtime/design/how-compose-works.md
new file mode 100644
index 0000000..c6d71f7
--- /dev/null
+++ b/compose/runtime/design/how-compose-works.md
@@ -0,0 +1,412 @@
+# How Composition Works
+
+Core to the runtime of Compose is the Composer. It is passed as an implicit parameter to every `@Composable` function and calls are inserted to Composer by the Compose Compiler Plugin.
+
+This document goes into more detail about how the Composer detects changes and what must be true about the code generated by the Compose Compiler Plugin for the algorithm it uses to work.
+
+## Purpose of the Composer
+
+The composer has three jobs,
+1. Record positional information for the composition (positional memoization)
+    - It records the results of calling the lambda function passed to `remember`
+    - It records the value of the parameters passed to skippable composable functions.
+2. Detect changes to composition
+3. Incrementally evaluate the composition as the data used to produce the composition changes
+
+## Positional Memoization
+
+Memoization is a technique which remembers, based on the actual parameters of the call, the previous result of a function call instead of recalculating it every time. Positional Memoization is memoization but also considers where in the call graph the function is executed as implicitly one of the actual parameters.
+
+Composition is the result of calling composable functions from some root composable function. The function calls, at runtime, form a tree. The location in this tree where the call to remember happens is considered an implicit parameter to remember. When the position is the same, the previous result of the lambda is used instead of calling it again. Consider the following example,
+
+##### Example: A and B with no parameters
+```kt
+@Composable
+fun A() {
+  val data = remember { Data() }
+  …
+}
+
+@Composable
+fun B() {
+  A()
+  A()
+}
+```
+
+When `B` is called the first time, there are two copies of `Data` created, one for each invocation of `A`. However, when `B` is called again, during recomposition, the lambdas are not invoked again, the previous values are returned instead.
+
+## The Slot Table
+
+*Note: the `SlotTable` is under active development and some of the following
+implementation details might be describe an older implementation*
+
+The Composer uses a side table to store information about the composable functions as they are executed that includes the results of the previous calls to `remember`, for example. This side table is often referred to as the Slot Table after the data structure used to hold it. The Slot Table records this information in a tree of groups flattened into two arrays, one storing information about the group and a second array storing the slot values (such as the result of calling `remember`). This is split into two arrays to avoid boxing overhead to store the 5 integer fields that make up the information stored about the group. Each group consists of a integer key, called Key, some Flags indicating what kind group it represents (is it a node group, for example); Nodes, the number of nodes generated in the group; Size, the size of the group (the total number of transitive children and itself); Slots, a reference to the location of the slots of the group in the slot array; and finally, Parent, an index of the parent group (to efficiently traverse the tree upward). These groups correspond to the call tree mentioned above in Positional Memoization where the position of positional memoization refers to the location of the group in the slot table generated by the call.
+
+The Slot Table of calling `B` above might look something like this,
+
+| Key  | Flags  | Nodes | Size  | Parent | Slots |
+|------|--------|-------|-------|--------|-------|
+| 1234 | <none> |   0   |   3   |   -1   |   0   |
+| 4567 | <none> |   0   |   1   |    0   |   0   |
+| 4567 | <none> |   0   |   1   |    0   |   1   |
+
+| Slots             |
+|-------------------|
+| <Data instance 0> |
+| <Data instance 1> |
+
+- Even though the groups records have 6 fields they are stored in 5 integers by packing Flags and Nodes together into a single integer.
+- The parent and size information are redundant. That is, parent can be calculated from size and size can be calculated from the parent but they are both stored to allow efficient traversal of the slot table from any node upwards and efficient pre-order enumeration of the tree.
+- There are no child pointers. A group is a child of its parent implicitly by its location in the array. The 4567 entries are both children of 1234 because 1234 is size 3 which includes both of them.
+- The number of slots assigned to a given group is the difference between its slot index and the next entry in the table. For the last group, the next index is assumed to be the size of the table. That is, `1234` has `0` slots because `0 - 0 ` is `0` both `4567` entries have one because `1 - 0 = 1` and `2 - 1 = 1`, respectively.
+- The `SlotTable` is read by a `SlotReader` instance and can be updated by a `SlotWriter` instance. Only one `SlotWriter` can be open at any time for a SlotTable but only when no `SlotReader` is open. Any number of `SlotReader` instances can be open at the same time and can only be opened if there is no open `SlotWriter` instance open.
+- When writing to the `SlotTable` the arrays of the slot table are a [gap buffer](https://en.wikipedia.org/wiki/Gap_buffer). The index of a group is the location ignoring the gap. The address of a group is its absolute location. To calculate the address of a group given an index, the address is index if the group is before the gap and `index + gap_size` if it is after the gap.  The meaning of the Slot field changes if the slot is referencing the slots after the gap in the slot array, its value is the distance to the end of the array. That is the slot address is `slot_array_size - slot`. Slot is updated as the slot array gap is moved. When an index value is used the way it is called values is called an anchor.
+- The number of slots associated with a group, once created, can not change when updating the slot table. That is, slots cannot be inserted, deleted or moved, only groups can. The value in the slot can only be updated. If slots are conditional then they must be in a group that is conditional.
+- When inserting a group, once a child group has been inserted, no further slots can be added to the parent group. If slots need to be added (because a call to `remember` after a call to a composable function, for example) then the slots require their own group.
+
+## Compiler Plugin
+The job of the compiler plugin is to transform `@Composable` calls and inject the calls into the runtime to allow it to generate the slot table.
+
+### Positional Memoization
+The above code is rewritten by the plugin (ignoring skipping and restarting, for now) to look something like,
+
+##### Example: Positional Memoization runtime calls
+```kt
+fun A($composer: Composer) {
+  $composer.startGroup(1234)
+  val data = remember($composer) { Data() }
+  …
+  $composer.endGroup()
+}
+
+@Composable
+fun B($composer: Composer) {
+  $composer.startGroup(4567)
+  A($composer)
+  A($composer)
+  $composer.endGroup()
+}
+```
+
+This code is sufficient to enable positional composition, but not skipping or restarting described
+
+### Skipping
+If the actual arguments of a function are the same as the last time it was called in the same position then it is assumed it will produce the same result and can be skipped. To determine if a function can be skipped, the previous values are remembered in the slot table and compared with the current values. If they are the same values, the function can be skipped.
+
+Since `A` and `B` don't take parameters they can be skipped unconditionally as they are assumed to always produce the same result.
+
+##### Example: Position Memoization and Skipping of parameterless functions
+```kt
+fun A($composer: Composer) {
+  $composer.startGroup(1234)
+  val data = remember($composer) { Data() }
+  if (!$composer.skipping) {
+    …
+  } else {
+    $composer.skipToEndGroup()
+  }
+  $composer.endGroup()
+}
+
+@Composable
+fun B($composer: Composer) {
+  $composer.startGroup(4567)
+  if (!$composer.skipping) {
+    A($composer)
+    A($composer)
+  } else {
+    $composer.skipToEndGroup()
+  }
+  $composer.endGroup()
+}
+```
+
+In this example `$composer.skipping` is `false` the first time `B` is invoked and `true` every subsequent call of `B` at the same position. The call to `$composer.skipToEndGroup()` requests the composer to skip the group end of `B`'s group which includes both calls to `A`.
+
+If the code was modified to have parameters then the parameters need to be checked.
+
+##### Example: A and B with parameters
+```kt
+@Composable
+fun A(text: String) {
+  val data = remember { Data() }
+  …
+}
+
+@Composable
+fun B(person: Person) {
+  A(person.givenName)
+  A(person.familyName)
+}
+```
+
+The resulting code might look something like,
+
+##### Example: A and B with skipping code that checks the parameter values.
+```kt
+fun A(text: String, $composer: Composer) {
+  $composer.startGroup(1234)
+  val data = remember($composer) { Data() }
+  val changed = $composer.changed(text)
+  if (changed || !$composer.skipping) {
+    …
+  } else {
+    $composer.skipToEndGroup()
+  }
+  $composer.endGroup()
+}
+
+@Composable
+fun B(person: Person, $composer: Composer) {
+  $composer.startGroup(4567)
+  val changed = $composer.changed(person)
+  if (changed || !$composer.skipping) {
+    A(person.givenName, $composer)
+    A(person.familyName, $composer)
+  } else {
+    $composer.skipToEndGroup()
+  }
+  $composer.endGroup()
+}
+```
+
+- `changed` uses a slot in the slot table to store values from the previous composition so that they may be compared.
+- As the number slots cannot change for the group, the number of times `changed` is called cannot be conditional, therefore a non-short-circuiting `||` pattern needs to be used.
+
+### Restarting
+The above code would be correct if `Person` was immutable. What if `Person` can change? If the value of `givenName` and `familyName` backed by `mutableStateOf`, then `B` must be called again, or restarted, whenever either `givenName` or `familyName` is modified. A restartable composable function is reported to the runtime using a `startRestartGroup` instead of just a `startGroup` call. For the above `B` this looks like,
+
+##### Example: A and B with parameters, positional memoization, skipping and restarting calls.
+```kt
+@Composable
+fun B(person: Person, $composer: Composer) {
+  $composer.startRestartGroup(4567)
+  val changed = $composer.changed(person)
+  if (changed || !$composer.skipping) {
+    A(person.givenName, $composer)
+    A(person.familyName, $composer)
+  } else {
+    $composer.skipToEndGroup()
+  }
+  $composer.endRestartGroup()?.updateScope { $composer: Composer -> B(person, $composer) }
+}
+```
+
+If the call to `B` needs to be restarted then the lambda passed to `updateScope` is invoked. `endRestartGroup()` will return `null` when no observable reads occurred in `B`. This means that the code above works correctly when `Person` is observable or immutable. If it is immutable then `endRestartGroup()` will return `null`, avoiding the creation of the lambda instance.
+
+It is important that the code work correctly if `Person` is immutable or mutable as the implementation of `Person` might be in a different module and might change its implementation after the module containing `B` has beend compiled. This means that the code generated for `B` cannot assume anything about the implementation of `Person` that is not in its type declaration.
+
+In the call `$composer.skipToEndGroup()`, if there are any functions that need to be restarted in the groups being skipped, the functions are restarted, by calling their restart lambdas, prior to `skipToEndGroup()` returning.
+
+### `$changed`
+The compiler plugin also adds an additional parameter `$changed` that is used to avoid calling `$composer.changed()` in cases where either the value will never change or was already compared by the caller and there is no need to do it again. How this works is beyond the scope of this document as it doesn't affect how the `Composer` works, only what code the plugin generates.
+
+## Detecting Structural Changes
+A structural change is detected when the groups emitted by one or more composable functions change.
+
+### Detecting Deletes
+Consider the following function,
+
+##### Example: ShowPerson
+```kt
+@Composable
+fun ShowPerson(person: Person) {
+  Column {
+    ShowName(person.name)
+    if (person.employed) {
+      ShowCompany(person.employer)
+    }
+    ShowEmail(person.email)
+  }
+}
+```
+
+If `person.employed` becomes `true` the `ShowCompany` content should be inserted just after `ShowName` but before `ShowEmail`. If it becomes `false`, `ShowCompany` should be deleted.
+
+A simplified slot table for a `true` version might look,
+
+|    # | Key             | Size | Nodes |
+|-----:|-----------------|-----:|------:|
+|    0 | `<ShowPerson>`  |    9 |     1 |
+|    1 | `<Column>`      |    8 |     1 |
+|    2 | Node            |    7 |     3 |
+|    3 | `<ShowName>`    |    2 |     1 |
+|    4 | Node            |    1 |     0 |
+|    5 | `<ShowCompany>` |    2 |     1 |
+|    6 | Node            |    1 |     0 |
+|    7 | `<ShowEmail>`   |    2 |     1 |
+|    8 | Node            |    1 |     0 |
+
+
+Where keys like `<ShowPerson>` is an arbitrary key generated by the compiler plugin for the `ShowPerson` function.
+
+The sequence of calls used to produce this table are,
+
+```
+startRestartGroup(<ShowPerson>)
+startGroup(<Column>)
+startNode()
+startRestartGroup(<ShowName>)
+startNode()
+endNode()
+endRestartGroup()
+startRestartGroup(<ShowCompany>)
+startNode()
+endNode()
+endRestartGroup()
+startRestartGroup(<ShowEmail>)
+startNode()
+endNode()
+endRestartGroup()
+endNode()
+endGroup()
+endRestartGroup()
+```
+
+From now on, this kind of sequence will be represented as follows,
+
+```
+<ShowPerson> <Column> N <ShowName> N/ /G <ShowCompany> N/ /G <ShowEmail> N/ /G /N /G /G
+```
+
+Where `<ShowPerson>` is a `startGroup(<ShowPerson>)`, `N` is a `startNode()`, `N/` is a `startNode()` followed immediately by an `endNode()`, `/G` is an the appropriate end group call, `/N` is an `endNode()`.
+
+The sequence when `employed` is `false` is:
+
+```
+<ShowPerson> <Column> N <ShowName> N/ /G <ShowEmail> N/ /G /N /G /G
+```
+
+During recomposition, the slot table from the previous composition is consulted as each group is generated and if the new table would match what is in the old table then nothing has changed. In this case, it isn't until group #5, the `<ShowCompany>` group, it detects a difference. At this point it is not clear whether this is an insert, delete or move, so the groups remaining in the current group of the old slot table (the `<Column>`'s Node group) are put into a hash table with the Key as the hash table key. `<ShowEmail>` is in the hash table so it's group is scheduled to move to position #5, then the `<ShowEmail>` group is set as the current group and `ShowEmail()` is called. No changes are detected for `ShowEmail()`. Next is `\G`. This means no further groups are coming for `<Column>` so any groups left in the hash table should be removed which schedules `<ShowCompany>` to be deleted. Since the group contains 1 node, 1 node is removed from index 1 of the node produced for `<Column>`. We know it is at index 1 because the previous group's node counts sum to 1.
+
+The hash table is stored in a `Pending` object created by the `Composer`. `Pending` also tracks where the pending group's nodes are relative to other changes that have already been made. For example, `Pending` is updated to record that `<ShowEmail>`'s node will be at index 1 after the `<ShowCompany>` node is removed so that, if it needs to be removed as well, the `Composer` removes the node at index 1 again. These adjacent removes are coalesced into a single call to the `Applier` that would remove two nodes at index 1.
+
+### Detecting Inserts
+After the slot table is updated to reflect the `false` state above, it would look something like this,
+
+| # | Key            | Size | Nodes |
+|--:|----------------|-----:|------:|
+| 0 | `<ShowPerson>` |    7 |     1 |
+| 1 | `<Column>`     |    6 |     1 |
+| 2 | Node           |    5 |     3 |
+| 3 | `<ShowName>`   |    2 |     1 |
+| 4 | Node           |    1 |     0 |
+| 5 | `<ShowEmail>`  |    2 |     1 |
+| 6 | Node           |    1 |     0 |
+
+If the value of `person.employed` becomes `true` again the original sequence is played to the composer,
+
+```
+<ShowPerson> <Column> N <ShowName> N/ /G <ShowCompany> N/ /G <ShowEmail> N/ /G /N /G /G
+```
+
+Just as in detecting deletes, the sequence is the same up to group #5. Here we encounter `<ShowCompany>` when we expect `<ShowEmail>`. As before we create a `Pending` object that contains the remaining groups which, in this case, is just `<ShowEmail>`. We consult `Pending` to see if `<ShowCompany>` is in this hash table. It isn't so this is an insert of new content. The composer switches to using  a side table, an insert table which is a separate slot table, to hold the new content and `ShowCompany` proceeds to execute adding groups to the insert table. The main slot table is not used to store the new groups because the slot table is read-only during composition. After `ShowCompany` executes, the insert table table might look something like,
+
+| # | Key             | Size | Nodes |
+|--:|-----------------|-----:|------:|
+| 0 | `<ShowCompany>` |    2 |     1 |
+| 1 | Node            |    1 |     0 |
+
+And it would be scheduled to be inserted at group #5. It contains a node so this node is scheduled to be inserted at index 1 of the `Column` node.
+
+### Detecting Moves
+Currently moves can only be generated by using a `key` composable. This adds an additional key object to the group generated for `key`. However, the runtime is more general and can handle non-key groups moving. As this is the case, to demonstrate how movement is detected we will simply use a different sequence for the order of the groups. Given the `true` slot table above, consider the runtime encountering following sequence,
+
+```
+<ShowPerson> <Column> N <ShowEmail> N/ /G <ShowCompany> N/ /G <ShowName> N/ /G /N /G /G
+```
+
+where order of the groups of the column are reversed. The sequence is the same until group #3. Just as before, a `Pending` object is created and the remaining groups are added. In this case `<ShowName>`, `<ShowCompany>` and `<ShowEmail>` are added. The pending object is consulted and `<ShowEmail>` is found and scheduled to be moved to group #3 (sliding the others down) and its node is scheduled to be moved to index 0 (moving `ShowName` to 1 and `ShowCompany` to 2). Next `<ShowCompany>` is in `Pending` so it is scheduled to be moved to group #5 (it is now would be at group #7 as it slid down) and its node is moved to index 1 from 2. Next `<ShowName>` is encountered. It has already slid down to index #7 and its nodes also have slid down to index 2 so nothing needs to be moved and no changes are scheduled. Just like deletes, adjacent moves are also coalesced so that nodes that move together result in a single call to the `Applier` to move the nodes.
+
+### Content Identity
+Call location is the identity of composable content. This principle follows from positional memoization. The state generated by a call site is assumed to update the state that was generated at the same call-site in the same position.
+
+Consider the following,
+
+##### Example: Counter
+```kt
+@Composable
+fun Counter() {
+  var count by mutableStateOf(0)
+  Row {
+    Text("Count: $count")
+    Button("+", onClick = { count++ })
+    Button("-", onClick = { count-- })
+  }
+}
+```
+
+This example uses `count` as an example state that is private and local to the composable function. This state might be animation state, focus, text selection, scroll position, etc. For all of these, the state is perceived as logically part of the content generated by the function, just as `count` is above.
+
+`Counter` can might be used like,
+
+##### Example: Simple Counters
+```kt
+@Composable
+fun Counters() {
+  Counter()
+  Counter()
+  Counter()
+}
+```
+
+The code implies there are three independent counter states created, one for each `Counter` call in `Counters`.  They each get their own instance of the count object; their own private state.
+
+If the code was changed to,
+
+##### Example: Counters with parameter
+```kt
+@Composable
+fun Counters(showMiddle: Boolean) {
+  Counter()
+  if (showMiddle) {
+    Counter()
+  }
+  Counter()
+}
+```
+
+toggling `showMiddle` from `true` to `false` must preserve the state of the last counter and remove only the middle counter. Toggling from `false` to `true` must insert a new counter in between the first and last counters. In other words, the identity of the counters is inferred from the call-site that created it. This implies, at minimum, the key for the group that contains the second counter must be different from the group that contains the third counter. If we were to rely entirely on the the group key generated for the by the `startRestartGroup()` call at the beginning `Counter` then the sequence the runtime would see for `Counter` with `true` would be,
+
+```
+<Counter> … /G <Counter> … /G <Counter> … /G
+```
+
+And if it was `false`,
+
+```
+<Counter> … /G <Counter> … /G
+```
+
+Following the algorithm above this would delete the last counter not the middle counter. If, on the other hand, a group was inserted around the if statement, the sequences would look like,
+
+```
+<Counter> … /G <if> <Counter> … /G /G <Counter> … /G
+<Counter> … /G <if> /G <Counter> … /G
+```
+
+which will correctly delete the state of the middle counter. It is the responsibility of the compiler plugin to generate the runtime calls necessary to ensure that groups generated by a call will be interpreted correctly, such as wrapping if statements in a group if necessary. Groups to resolve this ambiguity are called flow-control groups as they are inserted around language constructs that can change the flow of what code gets executed.
+
+### Duplicate keys
+When a `Pending` object is created, the `Pending` object will return the groups with the same key in the same order that the group was generated in the old slot table. Consider the following code that uses Counter from before,
+
+##### Example: Repeated Counters
+```kt
+@Composable
+fun Counters(count: Int) {
+  Row {
+    repeat(count) {
+      if (displayLabelFor(count)) {
+         Text("Counter #$count: ")
+      }
+      Counter()
+    }
+}
+```
+
+Assume this generates a sequence of the same key, <Counter> repeatedly, with <Text> periodically inserted whenever `displayLabelFor()` returns `true`.
+
+	<Counter> … /G <Counter> … /G … <Text> … /G … /G … <Counter> … /G
+
+If `displayLabelFor` changes from returning `true` for `count % 5 == 0` to `count % 3 == 0`, a change will be encountered when the first `<Text>` group is emitted. It, and all subsequent groups, are added to the `Pending` object which will contain only two hash table entries, one for all the `<Text>` groups and one for all the `<Counter>` groups. As each `<Counter>` is encountered it will select the same `<Counter>` as was previously generated because they are selected in the same order as they appear in the old slot table. This preserves the identity of the content generated by all calls to `Counter()`.  Using duplicate keys in order preserves the identity of all calls to `Counter()` regardless of what `displayLabelFor()` returns without requiring an explicit key for each loop iteration.
diff --git a/compose/runtime/design/images/call-tree-1.svg b/compose/runtime/design/images/call-tree-1.svg
new file mode 100644
index 0000000..a74c007
--- /dev/null
+++ b/compose/runtime/design/images/call-tree-1.svg
@@ -0,0 +1 @@
+<svg version="1.1" viewBox="0.0 0.0 437.0 122.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" preserveAspectRatio="none" width="437" height="122" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="id_0"><path d="M0 0L437.0 0L437.0 122.0L0 122.0L0 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M-7.0799766 -17.654459L719.8175 -17.654459L719.8175 527.9556L-7.0799766 527.9556Z" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M172.84384 0.28491777L237.80672 0.28491777L237.80672 30.429516L172.84384 30.429516Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M172.84384 0.28491777L237.80672 0.28491777L237.80672 30.429516L172.84384 30.429516Z" fill-rule="evenodd"/><clipPath id="id_1"><path d="M-2.842171E-14 0L437.0 0L437.0 122.0L-2.842171E-14 122.0L-2.842171E-14 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_1)" d="M194.96977 19.290155L194.96977 11.697436L198.33275 11.697436Q199.3443 11.697436 199.86781 11.901685Q200.4002 12.105933 200.71078 12.629875Q201.03021 13.144937 201.03021 13.775443Q201.03021 14.5746765 200.5067 15.13414Q199.98317 15.684724 198.89175 15.83569Q199.29106 16.031057 199.50401 16.217546Q199.93881 16.617163 200.32922 17.221027L201.65135 19.290155L200.39134 19.290155L199.38866 17.709448Q198.945 17.02566 198.66106 16.670444Q198.3771 16.306349 198.14641 16.164263Q197.92458 16.013298 197.69386 15.960015Q197.52528 15.915613 197.13486 15.915613L195.97246 15.915613L195.97246 19.290155L194.96977 19.290155ZM195.97246 15.054217L198.12866 15.054217Q198.82077 15.054217 199.20233 14.912131Q199.59274 14.761165 199.79683 14.450352Q200.00092 14.139539 200.00092 13.775443Q200.00092 13.233741 199.61049 12.887406Q199.22008 12.541072 198.36824 12.541072L195.97246 12.541072L195.97246 15.054217ZM202.14175 16.53724Q202.14175 15.009815 202.98471 14.281625Q203.69458 13.668879 204.715 13.668879Q205.85078 13.668879 206.56952 14.41483Q207.28825 15.151901 207.28825 16.466196Q207.28825 17.52296 206.96881 18.135706Q206.64937 18.739573 206.03712 19.077026Q205.43373 19.41448 204.715 19.41448Q203.56148 19.41448 202.85162 18.677408Q202.14175 17.931458 202.14175 16.53724ZM203.10007 16.53724Q203.10007 17.594004 203.55261 18.126825Q204.014 18.650768 204.715 18.650768Q205.40712 18.650768 205.86853 18.126825Q206.32994 17.594004 206.32994 16.510597Q206.32994 15.489355 205.86853 14.965413Q205.40712 14.441471 204.715 14.441471Q204.014 14.441471 203.55261 14.965413Q203.10007 15.480474 203.10007 16.53724ZM208.03401 16.53724Q208.03401 15.009815 208.87697 14.281625Q209.58684 13.668879 210.60725 13.668879Q211.74303 13.668879 212.46176 14.41483Q213.1805 15.151901 213.1805 16.466196Q213.1805 17.52296 212.86107 18.135706Q212.54163 18.739573 211.92937 19.077026Q211.32599 19.41448 210.60725 19.41448Q209.45374 19.41448 208.74387 18.677408Q208.03401 17.931458 208.03401 16.53724ZM208.99232 16.53724Q208.99232 17.594004 209.44485 18.126825Q209.90627 18.650768 210.60725 18.650768Q211.29938 18.650768 211.76077 18.126825Q212.22218 17.594004 212.22218 16.510597Q212.22218 15.489355 211.76077 14.965413Q211.29938 14.441471 210.60725 14.441471Q209.90627 14.441471 209.44485 14.965413Q208.99232 15.480474 208.99232 16.53724ZM216.30429 18.4554L216.4374 19.281275Q216.04697 19.361198 215.7364 19.361198Q215.23064 19.361198 214.94669 19.201351Q214.67162 19.041504 214.55626 18.783974Q214.44092 18.517563 214.44092 17.682808L214.44092 14.512514L213.75768 14.512514L213.75768 13.793204L214.44092 13.793204L214.44092 12.434507L215.36372 11.875044L215.36372 13.793204L216.30429 13.793204L216.30429 14.512514L215.36372 14.512514L215.36372 17.73609Q215.36372 18.126825 215.4081 18.242271Q215.46133 18.357716 215.56781 18.428759Q215.68317 18.490921 215.89613 18.490921Q216.04697 18.490921 216.30429 18.4554Z" fill-rule="nonzero"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M70.334236 44.048885L101.06283 44.048885L101.06283 74.19348L70.334236 74.19348Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M70.334236 44.048885L101.06283 44.048885L101.06283 74.19348L70.334236 74.19348Z" fill-rule="evenodd"/><clipPath id="id_2"><path d="M0 7.1054274E-15L437.0 7.1054274E-15L437.0 122.0L0 122.0L0 7.1054274E-15Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_2)" d="M82.1475 63.054123L85.05793 55.461403L86.14047 55.461403L89.24611 63.054123L88.10146 63.054123L87.22301 60.754105L84.04638 60.754105L83.212296 63.054123L82.1475 63.054123ZM84.339195 59.93711L86.912445 59.93711L86.11385 57.832462Q85.75892 56.873383 85.58145 56.260635Q85.43061 56.988827 85.17329 57.708138L84.339195 59.93711Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M205.32529 30.429516L85.701996 44.05187" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M205.32529 30.429516L85.701996 44.05187" fill-rule="evenodd"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M0.28468958 91.56601L31.013279 91.56601L31.013279 121.71061L0.28468958 121.71061Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M0.28468958 91.56601L31.013279 91.56601L31.013279 121.71061L0.28468958 121.71061Z" fill-rule="evenodd"/><clipPath id="id_3"><path d="M-5.551115E-17 0L437.0 0L437.0 122.0L-5.551115E-17 122.0L-5.551115E-17 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_3)" d="M12.887676 110.57124L12.887676 102.97852L15.735992 102.97852Q16.605572 102.97852 17.129095 103.20942Q17.652617 103.44031 17.945435 103.919846Q18.247126 104.39938 18.247126 104.92333Q18.247126 105.41175 17.980927 105.846886Q17.723602 106.27315 17.191208 106.53068Q17.874449 106.734924 18.247126 107.22334Q18.619802 107.71177 18.619802 108.36891Q18.619802 108.90173 18.389097 109.36352Q18.167265 109.816414 17.830082 110.06506Q17.501772 110.31371 17.00487 110.44692Q16.507965 110.57124 15.780358 110.57124L12.887676 110.57124ZM13.899227 106.16658L15.531907 106.16658Q16.206276 106.16658 16.49022 106.086655Q16.871769 105.971214 17.066982 105.7048Q17.262194 105.43839 17.262194 105.03877Q17.262194 104.665794 17.075855 104.38162Q16.89839 104.08858 16.561205 103.98201Q16.224022 103.87544 15.407681 103.87544L13.899227 103.87544L13.899227 106.16658ZM13.899227 109.674324L15.780358 109.674324Q16.268389 109.674324 16.4636 109.63881Q16.809656 109.576645 17.040361 109.434555Q17.27994 109.28359 17.430784 109.0083Q17.58163 108.73301 17.58163 108.36891Q17.58163 107.94266 17.359798 107.63184Q17.146841 107.32103 16.756418 107.1967Q16.374866 107.0635 15.64726 107.0635L13.899227 107.0635L13.899227 109.674324Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M69.96954 91.56601L100.698135 91.56601L100.698135 121.71061L69.96954 121.71061Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M69.96954 91.56601L100.698135 91.56601L100.698135 121.71061L69.96954 121.71061Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M82.57253 110.57124L82.57253 102.97852L85.420845 102.97852Q86.29043 102.97852 86.81395 103.20942Q87.33747 103.44031 87.63029 103.919846Q87.931984 104.39938 87.931984 104.92333Q87.931984 105.41175 87.66578 105.846886Q87.408455 106.27315 86.87606 106.53068Q87.5593 106.734924 87.931984 107.22334Q88.30466 107.71177 88.30466 108.36891Q88.30466 108.90173 88.07395 109.36352Q87.85212 109.816414 87.51494 110.06506Q87.18663 110.31371 86.68972 110.44692Q86.19282 110.57124 85.46522 110.57124L82.57253 110.57124ZM83.58408 106.16658L85.21677 106.16658Q85.89113 106.16658 86.17507 106.086655Q86.556625 105.971214 86.75184 105.7048Q86.94705 105.43839 86.94705 105.03877Q86.94705 104.665794 86.76071 104.38162Q86.583244 104.08858 86.24606 103.98201Q85.908875 103.87544 85.09254 103.87544L83.58408 103.87544L83.58408 106.16658ZM83.58408 109.674324L85.46522 109.674324Q85.95325 109.674324 86.14845 109.63881Q86.494514 109.576645 86.72522 109.434555Q86.9648 109.28359 87.11564 109.0083Q87.26649 108.73301 87.26649 108.36891Q87.26649 107.94266 87.044655 107.63184Q86.831696 107.32103 86.44127 107.1967Q86.05972 107.0635 85.332115 107.0635L83.58408 107.0635L83.58408 109.674324Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M142.1145 91.56601L172.8431 91.56601L172.8431 121.71061L142.1145 121.71061Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M142.1145 91.56601L172.8431 91.56601L172.8431 121.71061L142.1145 121.71061Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M159.88226 107.907135L160.88493 108.164665Q160.5655 109.39903 159.74916 110.05618Q158.93283 110.70445 157.7438 110.70445Q156.5193 110.70445 155.74733 110.20715Q154.98422 109.700966 154.57605 108.75965Q154.17676 107.80945 154.17676 106.71716Q154.17676 105.53607 154.62929 104.65691Q155.08183 103.76888 155.91591 103.31598Q156.75888 102.8542 157.76155 102.8542Q158.89732 102.8542 159.66931 103.43143Q160.45015 104.00865 160.75185 105.065414L159.7669 105.2963Q159.50072 104.47043 158.99493 104.09745Q158.49803 103.7156 157.7438 103.7156Q156.86536 103.7156 156.27972 104.13297Q155.69408 104.550354 155.4545 105.26078Q155.21492 105.96233 155.21492 106.71716Q155.21492 107.68513 155.49887 108.404434Q155.78282 109.12374 156.37732 109.48784Q156.97183 109.843056 157.66394 109.843056Q158.50691 109.843056 159.09254 109.35464Q159.67818 108.86621 159.88226 107.907135Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M85.69853 74.19348L15.655951 91.57496" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M85.69853 74.19348L15.655951 91.57496" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M85.69853 74.19348L85.340805 91.57496" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M85.69853 74.19348L85.340805 91.57496" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M85.69853 74.19348L157.47607 91.57496" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M85.69853 74.19348L157.47607 91.57496" fill-rule="evenodd"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M110.5023 50.43343L128.49586 50.43343L128.49586 67.8149L110.5023 67.8149Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M110.5023 50.43343L128.49586 50.43343L128.49586 67.8149L110.5023 67.8149Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M119.89908 62.3822Q119.37556 62.817333 118.8964 63.003822Q118.42612 63.18143 117.87598 63.18143Q116.9709 63.18143 116.48287 62.73741Q115.99484 62.293392 115.99484 61.609604Q115.99484 61.201107 116.18118 60.87253Q116.36752 60.53508 116.66034 60.33971Q116.96203 60.135464 117.33471 60.028896Q117.60091 59.957855 118.15992 59.88681Q119.28683 59.753605 119.81922 59.56712Q119.828094 59.37175 119.828094 59.327347Q119.828094 58.759007 119.5619 58.519234Q119.20696 58.20842 118.4971 58.20842Q117.840485 58.20842 117.52992 58.439312Q117.21935 58.6702 117.06851 59.247425L116.154564 59.1231Q116.278786 58.545876 116.56274 58.190662Q116.84668 57.826565 117.38795 57.6312Q117.929214 57.43583 118.6302 57.43583Q119.340065 57.43583 119.77486 57.604557Q120.21851 57.764404 120.4226 58.021935Q120.63556 58.270584 120.715416 58.65244Q120.75979 58.892212 120.75979 59.513836L120.75979 60.757088Q120.75979 62.053623 120.8219 62.399956Q120.88401 62.73741 121.052605 63.057106L120.08542 63.057106Q119.93457 62.764053 119.89908 62.3822ZM119.81922 60.29531Q119.313446 60.50844 118.301895 60.650524Q117.72513 60.730446 117.48555 60.837013Q117.24597 60.943577 117.11288 61.138943Q116.98865 61.334312 116.98865 61.582962Q116.98865 61.95594 117.2726 62.20459Q117.55654 62.45324 118.09781 62.45324Q118.6302 62.45324 119.04725 62.22235Q119.47316 61.98258 119.66837 61.57408Q119.81922 61.25439 119.81922 60.641644L119.81922 60.29531Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M101.06283 59.121185L110.50677 59.121185" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M101.06283 59.121185L110.50677 59.121185" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M344.27615 65.50573L356.04532 65.50573" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M344.27615 65.50573L356.04532 65.50573" fill-rule="evenodd"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M42.004738 97.94757L59.998314 97.94757L59.998314 115.32905L42.004738 115.32905Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M42.004738 97.94757L59.998314 97.94757L59.998314 115.32905L42.004738 115.32905Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M48.668556 110.57124L47.80785 110.57124L47.80785 102.97853L48.739544 102.97853L48.739544 105.68704Q49.334053 104.94997 50.247997 104.94997Q50.753773 104.94997 51.20631 105.15422Q51.658848 105.35847 51.951664 105.731445Q52.24448 106.095535 52.4042 106.61948Q52.572792 107.143425 52.572792 107.7384Q52.572792 109.15038 51.871803 109.92298Q51.17969 110.69557 50.19476 110.69557Q49.227573 110.69557 48.668556 109.87858L48.668556 110.57124ZM48.659683 107.78281Q48.659683 108.76853 48.934757 109.21255Q49.369545 109.93186 50.12377 109.93186Q50.736027 109.93186 51.17969 109.39904Q51.623352 108.86621 51.623352 107.81833Q51.623352 106.743805 51.197437 106.228745Q50.77152 105.713684 50.159264 105.713684Q49.555885 105.713684 49.103348 106.246506Q48.659683 106.77933 48.659683 107.78281Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M31.013279 106.638306L42.013325 106.638306" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M31.013279 106.638306L42.013325 106.638306" fill-rule="evenodd"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M112.752365 97.94757L130.74594 97.94757L130.74594 115.32905L112.752365 115.32905Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M112.752365 97.94757L130.74594 97.94757L130.74594 115.32905L112.752365 115.32905Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M119.41618 110.57124L118.55548 110.57124L118.55548 102.97853L119.487175 102.97853L119.487175 105.68704Q120.08168 104.94997 120.99563 104.94997Q121.5014 104.94997 121.95394 105.15422Q122.40648 105.35847 122.699295 105.731445Q122.99211 106.095535 123.15183 106.61948Q123.32042 107.143425 123.32042 107.7384Q123.32042 109.15038 122.61943 109.92298Q121.92732 110.69557 120.94239 110.69557Q119.975204 110.69557 119.41618 109.87858L119.41618 110.57124ZM119.40731 107.78281Q119.40731 108.76853 119.68239 109.21255Q120.11717 109.93186 120.8714 109.93186Q121.48366 109.93186 121.92732 109.39904Q122.37098 108.86621 122.37098 107.81833Q122.37098 106.743805 121.94507 106.228745Q121.51915 105.713684 120.9069 105.713684Q120.30351 105.713684 119.850975 106.246506Q119.40731 106.77933 119.40731 107.78281Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M100.698135 106.638306L112.75347 106.638306" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M100.698135 106.638306L112.75347 106.638306" fill-rule="evenodd"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M184.75777 97.94757L202.75134 97.94757L202.75134 115.32905L184.75777 115.32905Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M184.75777 97.94757L202.75134 97.94757L202.75134 115.32905L184.75777 115.32905Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M194.15456 108.555405L195.0685 108.679726Q194.91765 109.62105 194.29652 110.16275Q193.68427 110.69557 192.7792 110.69557Q191.6523 110.69557 190.96906 109.9585Q190.28581 109.22143 190.28581 107.84497Q190.28581 106.94805 190.57863 106.28203Q190.87144 105.616 191.47482 105.28742Q192.07822 104.94997 192.78807 104.94997Q193.68427 104.94997 194.25217 105.40287Q194.82005 105.85577 194.97977 106.69052L194.07469 106.82373Q193.94159 106.27315 193.61328 105.997856Q193.28497 105.713684 192.82356 105.713684Q192.12257 105.713684 191.67891 106.219864Q191.23524 106.72604 191.23524 107.81833Q191.23524 108.928375 191.66116 109.434555Q192.08708 109.93186 192.77032 109.93186Q193.32047 109.93186 193.68427 109.594406Q194.05695 109.25695 194.15456 108.555405Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M172.8431 106.638306L184.75534 106.638306" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M172.8431 106.638306L184.75534 106.638306" fill-rule="evenodd"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M304.2984 44.05336L335.02698 44.05336L335.02698 74.19796L304.2984 74.19796Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M304.2984 44.05336L335.02698 44.05336L335.02698 74.19796L304.2984 74.19796Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M316.11166 63.058598L319.0221 55.465878L320.1046 55.465878L323.21027 63.058598L322.0656 63.058598L321.18716 60.75858L318.01053 60.75858L317.17645 63.058598L316.11166 63.058598ZM318.30334 59.941586L320.87662 59.941586L320.078 57.836937Q319.72308 56.877857 319.54562 56.26511Q319.39478 56.9933 319.13745 57.712612L318.30334 59.941586Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M234.24886 91.57048L264.97745 91.57048L264.97745 121.71508L234.24886 121.71508Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M234.24886 91.57048L264.97745 91.57048L264.97745 121.71508L234.24886 121.71508Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M246.85184 110.57572L246.85184 102.983L249.70015 102.983Q250.56973 102.983 251.09325 103.21389Q251.61678 103.44478 251.90959 103.924324Q252.21129 104.40386 252.21129 104.9278Q252.21129 105.41622 251.94508 105.851364Q251.68776 106.27762 251.15536 106.53515Q251.83861 106.7394 252.21129 107.22782Q252.58395 107.71624 252.58395 108.37339Q252.58395 108.90621 252.35326 109.36799Q252.13142 109.82089 251.79424 110.06954Q251.46593 110.31819 250.96902 110.45139Q250.47212 110.57572 249.74452 110.57572L246.85184 110.57572ZM247.86339 106.17106L249.49606 106.17106Q250.17044 106.17106 250.45438 106.09113Q250.83592 105.975685 251.03114 105.709274Q251.22635 105.44286 251.22635 105.04325Q251.22635 104.67027 251.04001 104.3861Q250.86255 104.09305 250.52536 103.98648Q250.18819 103.87992 249.37184 103.87992L247.86339 103.87992L247.86339 106.17106ZM247.86339 109.6788L249.74452 109.6788Q250.23254 109.6788 250.42776 109.64328Q250.77382 109.581116 251.00452 109.43903Q251.2441 109.28806 251.39494 109.01277Q251.54579 108.73748 251.54579 108.37339Q251.54579 107.94713 251.32396 107.636314Q251.111 107.3255 250.72058 107.20118Q250.33902 107.06797 249.61142 107.06797L247.86339 107.06797L247.86339 109.6788Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M303.93372 91.57048L334.6623 91.57048L334.6623 121.71508L303.93372 121.71508Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M303.93372 91.57048L334.6623 91.57048L334.6623 121.71508L303.93372 121.71508Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M316.53668 110.57572L316.53668 102.983L319.385 102.983Q320.25458 102.983 320.7781 103.21389Q321.30164 103.44478 321.59445 103.924324Q321.89615 104.40386 321.89615 104.9278Q321.89615 105.41622 321.62994 105.851364Q321.37262 106.27762 320.8402 106.53515Q321.52347 106.7394 321.89615 107.22782Q322.26883 107.71624 322.26883 108.37339Q322.26883 108.90621 322.03812 109.36799Q321.81628 109.82089 321.4791 110.06954Q321.1508 110.31819 320.65387 110.45139Q320.15698 110.57572 319.42938 110.57572L316.53668 110.57572ZM317.54825 106.17106L319.1809 106.17106Q319.8553 106.17106 320.13922 106.09113Q320.52078 105.975685 320.716 105.709274Q320.9112 105.44286 320.9112 105.04325Q320.9112 104.67027 320.72488 104.3861Q320.5474 104.09305 320.2102 103.98648Q319.87305 103.87992 319.0567 103.87992L317.54825 103.87992L317.54825 106.17106ZM317.54825 109.6788L319.42938 109.6788Q319.9174 109.6788 320.1126 109.64328Q320.45868 109.581116 320.68936 109.43903Q320.92896 109.28806 321.0798 109.01277Q321.23065 108.73748 321.23065 108.37339Q321.23065 107.94713 321.00882 107.636314Q320.79587 107.3255 320.40543 107.20118Q320.0239 107.06797 319.29626 107.06797L317.54825 107.06797L317.54825 109.6788Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M376.07867 91.57048L406.80725 91.57048L406.80725 121.71508L376.07867 121.71508Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M376.07867 91.57048L406.80725 91.57048L406.80725 121.71508L376.07867 121.71508Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M388.43375 110.57572L388.43375 102.983L391.05133 102.983Q391.9298 102.983 392.4001 103.09845Q393.04782 103.24053 393.50925 103.64015Q394.1126 104.14633 394.40543 104.93668Q394.70712 105.727036 394.70712 106.7394Q394.70712 107.6008 394.50305 108.2757Q394.29895 108.941734 393.97952 109.37687Q393.66895 109.81201 393.29626 110.06954Q392.9236 110.31819 392.3912 110.45139Q391.85883 110.57572 391.17557 110.57572L388.43375 110.57572ZM389.4364 109.6788L391.0602 109.6788Q391.80557 109.6788 392.23148 109.5456Q392.6574 109.40351 392.91473 109.14598Q393.26965 108.790764 393.46487 108.1869Q393.66895 107.58304 393.66895 106.72164Q393.66895 105.53167 393.27853 104.89228Q392.88812 104.2529 392.3291 104.039764Q391.9298 103.87992 391.0336 103.87992L389.4364 103.87992L389.4364 109.6788Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M319.6627 74.19796L249.62012 91.57944" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M319.6627 74.19796L249.62012 91.57944" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M319.6627 74.19796L319.30496 91.57944" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M319.6627 74.19796L319.30496 91.57944" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M319.6627 74.19796L391.44025 91.57944" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M319.6627 74.19796L391.44025 91.57944" fill-rule="evenodd"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M344.46646 50.437904L362.46002 50.437904L362.46002 67.81938L344.46646 67.81938Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M344.46646 50.437904L362.46002 50.437904L362.46002 67.81938L344.46646 67.81938Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M353.86325 62.386673Q353.33972 62.82181 352.86057 63.008297Q352.3903 63.185905 351.84015 63.185905Q350.93506 63.185905 350.44702 62.741886Q349.959 62.297867 349.959 61.61408Q349.959 61.20558 350.14536 60.87701Q350.3317 60.539555 350.6245 60.344185Q350.92618 60.13994 351.29886 60.033375Q351.56506 59.96233 352.12408 59.89129Q353.25098 59.758083 353.7834 59.571594Q353.79227 59.376225 353.79227 59.331825Q353.79227 58.76348 353.52606 58.52371Q353.1711 58.2129 352.46127 58.2129Q351.80466 58.2129 351.49408 58.443787Q351.1835 58.674675 351.03265 59.2519L350.1187 59.127575Q350.24295 58.55035 350.5269 58.195137Q350.81085 57.83104 351.3521 57.635674Q351.89337 57.440304 352.59436 57.440304Q353.30423 57.440304 353.739 57.60903Q354.18268 57.76888 354.38675 58.02641Q354.59973 58.27506 354.67957 58.656914Q354.72394 58.896687 354.72394 59.51831L354.72394 60.761562Q354.72394 62.058098 354.78607 62.404434Q354.84818 62.741886 355.01675 63.06158L354.0496 63.06158Q353.89874 62.768528 353.86325 62.386673ZM353.7834 60.299786Q353.27762 60.512913 352.26605 60.655Q351.6893 60.734924 351.4497 60.841488Q351.21014 60.94805 351.07703 61.14342Q350.95282 61.338787 350.95282 61.587437Q350.95282 61.960415 351.23676 62.209064Q351.5207 62.457714 352.06198 62.457714Q352.59436 62.457714 353.0114 62.226826Q353.43732 61.987057 353.63254 61.578556Q353.7834 61.258865 353.7834 60.64612L353.7834 60.299786Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M335.02698 59.12566L344.47092 59.12566" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M335.02698 59.12566L344.47092 59.12566" fill-rule="evenodd"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M275.9689 97.95204L293.96246 97.95204L293.96246 115.33352L275.9689 115.33352Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M275.9689 97.95204L293.96246 97.95204L293.96246 115.33352L275.9689 115.33352Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M282.63272 110.57572L281.772 110.57572L281.772 102.983L282.7037 102.983L282.7037 105.69151Q283.29822 104.954445 284.21216 104.954445Q284.71793 104.954445 285.17047 105.15869Q285.62302 105.362946 285.91583 105.735916Q286.20865 106.10001 286.36835 106.623955Q286.53696 107.147896 286.53696 107.74288Q286.53696 109.15486 285.83597 109.92745Q285.14386 110.70004 284.1589 110.70004Q283.19174 110.70004 282.63272 109.88305L282.63272 110.57572ZM282.62384 107.787285Q282.62384 108.773 282.89893 109.217026Q283.3337 109.93633 284.08792 109.93633Q284.7002 109.93633 285.14386 109.40351Q285.58752 108.87069 285.58752 107.82281Q285.58752 106.74828 285.1616 106.233215Q284.7357 105.718155 284.1234 105.718155Q283.52005 105.718155 283.0675 106.25098Q282.62384 106.7838 282.62384 107.787285Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M264.97745 106.642784L275.97748 106.642784" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M264.97745 106.642784L275.97748 106.642784" fill-rule="evenodd"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M346.71652 97.95204L364.7101 97.95204L364.7101 115.33352L346.71652 115.33352Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M346.71652 97.95204L364.7101 97.95204L364.7101 115.33352L346.71652 115.33352Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M353.38034 110.57572L352.51965 110.57572L352.51965 102.983L353.45132 102.983L353.45132 105.69151Q354.04584 104.954445 354.95978 104.954445Q355.46558 104.954445 355.9181 105.15869Q356.37064 105.362946 356.66345 105.735916Q356.95627 106.10001 357.116 106.623955Q357.28458 107.147896 357.28458 107.74288Q357.28458 109.15486 356.5836 109.92745Q355.89148 110.70004 354.90656 110.70004Q353.93936 110.70004 353.38034 109.88305L353.38034 110.57572ZM353.37146 107.787285Q353.37146 108.773 353.64655 109.217026Q354.08133 109.93633 354.83557 109.93633Q355.4478 109.93633 355.89148 109.40351Q356.33514 108.87069 356.33514 107.82281Q356.33514 106.74828 355.9092 106.233215Q355.4833 105.718155 354.87106 105.718155Q354.26767 105.718155 353.81512 106.25098Q353.37146 106.7838 353.37146 107.787285Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M334.6623 106.642784L346.71762 106.642784" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M334.6623 106.642784L346.71762 106.642784" fill-rule="evenodd"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M418.72192 97.95204L436.7155 97.95204L436.7155 115.33352L418.72192 115.33352Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M418.72192 97.95204L436.7155 97.95204L436.7155 115.33352L418.72192 115.33352Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M428.0921 110.57572L428.0921 109.88305Q427.57745 110.70004 426.557 110.70004Q425.9004 110.70004 425.35025 110.33595Q424.8001 109.971855 424.4984 109.323586Q424.19675 108.67532 424.19675 107.83169Q424.19675 107.00581 424.4718 106.33978Q424.7469 105.66487 425.28815 105.30966Q425.8383 104.954445 426.5215 104.954445Q427.01843 104.954445 427.4 105.16757Q427.7904 105.371826 428.02997 105.709274L428.02997 102.983L428.96167 102.983L428.96167 110.57572L428.0921 110.57572ZM425.15503 107.83169Q425.15503 108.88845 425.5987 109.41239Q426.04236 109.93633 426.64575 109.93633Q427.258 109.93633 427.68393 109.43903Q428.10983 108.93285 428.10983 107.911606Q428.10983 106.7838 427.67505 106.25986Q427.24026 105.727036 426.60138 105.727036Q425.98914 105.727036 425.57208 106.233215Q425.15503 106.7394 425.15503 107.83169Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M406.80725 106.642784L418.7195 106.642784" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M406.80725 106.642784L418.7195 106.642784" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M205.32529 30.429516L319.65424 44.05187" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M205.32529 30.429516L319.65424 44.05187" fill-rule="evenodd"/></svg>
\ No newline at end of file
diff --git a/compose/runtime/design/images/call-tree-2.svg b/compose/runtime/design/images/call-tree-2.svg
new file mode 100644
index 0000000..53d7530
--- /dev/null
+++ b/compose/runtime/design/images/call-tree-2.svg
@@ -0,0 +1 @@
+<svg version="1.1" viewBox="0.0 0.0 437.0 210.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" preserveAspectRatio="none" width="437" height="210" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="id_0"><path d="M0 0L437.0 0L437.0 210.0L0 210.0L0 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M-7.1110463 -9.217837L722.9763 -9.217837L722.9763 539.0853L-7.1110463 539.0853Z" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M148.66579 0.2863241L213.91374 0.2863241L213.91374 30.579716L148.66579 30.579716Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M148.66579 0.2863241L213.91374 0.2863241L213.91374 30.579716L148.66579 30.579716Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M170.88882 19.38537L170.88882 11.755175L174.26654 11.755175Q175.28253 11.755175 175.80835 11.960431Q176.34308 12.165688 176.65501 12.692216Q176.97585 13.20982 176.97585 13.843438Q176.97585 14.646617 176.45003 15.208842Q175.92421 15.762143 174.82802 15.913855Q175.22906 16.110188 175.44295 16.297594Q175.87965 16.699184 176.27179 17.30603L177.5997 19.38537L176.33417 19.38537L175.32709 17.796862Q174.88148 17.109697 174.5963 16.75273Q174.31111 16.386837 174.07939 16.24405Q173.85658 16.092339 173.62486 16.038794Q173.45554 15.994172 173.0634 15.994172L171.8959 15.994172L171.8959 19.38537L170.88882 19.38537ZM171.8959 15.128524L174.06157 15.128524Q174.75671 15.128524 175.13994 14.985737Q175.53207 14.834025 175.73706 14.521678Q175.94203 14.209331 175.94203 13.843438Q175.94203 13.299062 175.5499 12.951018Q175.15776 12.602974 174.30219 12.602974L171.8959 12.602974L171.8959 15.128524ZM178.09227 16.618866Q178.09227 15.083903 178.93893 14.3521185Q179.6519 13.736348 180.6768 13.736348Q181.81757 13.736348 182.53946 14.485981Q183.26135 15.22669 183.26135 16.547472Q183.26135 17.609453 182.9405 18.225224Q182.61967 18.83207 182.00473 19.171188Q181.3987 19.51031 180.6768 19.51031Q179.51822 19.51031 178.80525 18.7696Q178.09227 18.019966 178.09227 16.618866ZM179.0548 16.618866Q179.0548 17.680847 179.50931 18.2163Q179.97275 18.742826 180.6768 18.742826Q181.37196 18.742826 181.83539 18.2163Q182.29883 17.680847 182.29883 16.592094Q182.29883 15.56581 181.83539 15.039282Q181.37196 14.5127535 180.6768 14.5127535Q179.97275 14.5127535 179.50931 15.039282Q179.0548 15.556886 179.0548 16.618866ZM184.01038 16.618866Q184.01038 15.083903 184.85704 14.3521185Q185.57002 13.736348 186.59492 13.736348Q187.73569 13.736348 188.45757 14.485981Q189.17946 15.22669 189.17946 16.547472Q189.17946 17.609453 188.85861 18.225224Q188.53778 18.83207 187.92284 19.171188Q187.3168 19.51031 186.59492 19.51031Q185.43634 19.51031 184.72336 18.7696Q184.01038 18.019966 184.01038 16.618866ZM184.9729 16.618866Q184.9729 17.680847 185.42741 18.2163Q185.89085 18.742826 186.59492 18.742826Q187.29007 18.742826 187.75351 18.2163Q188.21693 17.680847 188.21693 16.592094Q188.21693 15.56581 187.75351 15.039282Q187.29007 14.5127535 186.59492 14.5127535Q185.89085 14.5127535 185.42741 15.039282Q184.9729 15.556886 184.9729 16.618866ZM192.31696 18.546495L192.45064 19.376446Q192.0585 19.456764 191.74658 19.456764Q191.23859 19.456764 190.9534 19.296127Q190.67711 19.135492 190.56126 18.87669Q190.4454 18.608965 190.4454 17.770088L190.4454 14.584147L189.75916 14.584147L189.75916 13.861287L190.4454 13.861287L190.4454 12.495884L191.37227 11.933659L191.37227 13.861287L192.31696 13.861287L192.31696 14.584147L191.37227 14.584147L191.37227 17.823635Q191.37227 18.2163 191.41682 18.332314Q191.4703 18.448328 191.57726 18.519722Q191.6931 18.582191 191.907 18.582191Q192.0585 18.582191 192.31696 18.546495Z" fill-rule="nonzero"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M41.065918 52.790073L71.92936 52.790073L71.92936 83.083466L41.065918 83.083466Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M41.065918 52.790073L71.92936 52.790073L71.92936 83.083466L41.065918 83.083466Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M52.931026 71.889114L55.85423 64.25892L56.941517 64.25892L60.060787 71.889114L58.911114 71.889114L58.028805 69.57775L54.838238 69.57775L54.000492 71.889114L52.931026 71.889114ZM55.13234 68.75672L57.716877 68.75672L56.91478 66.641685Q56.558292 65.67787 56.380047 65.0621Q56.22854 65.793884 55.970085 66.51675L55.13234 68.75672Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M181.28976 30.579716L56.50662 52.796066" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M181.28976 30.579716L56.50662 52.796066" fill-rule="evenodd"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M0.28593892 100.54174L31.149378 100.54174L31.149378 130.83513L0.28593892 130.83513Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M0.28593892 100.54174L31.149378 100.54174L31.149378 130.83513L0.28593892 130.83513Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M12.944232 119.640785L12.944232 112.01059L15.805048 112.01059Q16.678444 112.01059 17.204264 112.242615Q17.730083 112.47465 18.024187 112.95656Q18.327202 113.43846 18.327202 113.96499Q18.327202 114.45582 18.059835 114.893105Q17.801382 115.32147 17.26665 115.58027Q17.952888 115.78553 18.327202 116.27636Q18.701513 116.76719 18.701513 117.42758Q18.701513 117.963036 18.469795 118.42709Q18.24699 118.882225 17.908327 119.1321Q17.578577 119.38198 17.079493 119.51585Q16.58041 119.640785 15.849609 119.640785L12.944232 119.640785ZM13.960223 115.21438L15.600068 115.21438Q16.277395 115.21438 16.562586 115.13406Q16.94581 115.01804 17.141878 114.75032Q17.337946 114.4826 17.337946 114.081Q17.337946 113.706184 17.150791 113.420616Q16.972548 113.126114 16.633883 113.01903Q16.29522 112.911934 15.475297 112.911934L13.960223 112.911934L13.960223 115.21438ZM13.960223 118.73944L15.849609 118.73944Q16.33978 118.73944 16.535849 118.70374Q16.883425 118.64127 17.11514 118.49849Q17.355772 118.34677 17.507278 118.07012Q17.658787 117.79347 17.658787 117.42758Q17.658787 116.99922 17.435982 116.686874Q17.222088 116.37453 16.829952 116.24959Q16.446728 116.11572 15.715926 116.11572L13.960223 116.11572L13.960223 118.73944Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M71.92936 100.550735L102.79279 100.550735L102.79279 130.84413L71.92936 130.84413Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M71.92936 100.550735L102.79279 100.550735L102.79279 130.84413L71.92936 130.84413Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M84.587654 119.64978L84.587654 112.019585L87.44846 112.019585Q88.32186 112.019585 88.84768 112.25161Q89.373505 112.48364 89.6676 112.965546Q89.97062 113.44746 89.97062 113.97398Q89.97062 114.46481 89.703255 114.9021Q89.4448 115.33047 88.910065 115.589264Q89.596306 115.794525 89.97062 116.285355Q90.34493 116.776184 90.34493 117.43658Q90.34493 117.97203 90.11321 118.43609Q89.89041 118.89122 89.55174 119.1411Q89.22199 119.390976 88.72291 119.52484Q88.22383 119.64978 87.49303 119.64978L84.587654 119.64978ZM85.60364 115.22337L87.243484 115.22337Q87.920815 115.22337 88.206 115.14306Q88.589226 115.02704 88.78529 114.759315Q88.98136 114.491585 88.98136 114.09Q88.98136 113.71518 88.79421 113.42961Q88.61597 113.13511 88.2773 113.028015Q87.93864 112.92093 87.11871 112.92093L85.60364 112.92093L85.60364 115.22337ZM85.60364 118.748436L87.49303 118.748436Q87.9832 118.748436 88.17927 118.71274Q88.52684 118.65027 88.75856 118.507484Q88.99919 118.35577 89.150696 118.07912Q89.30221 117.80247 89.30221 117.43658Q89.30221 117.00822 89.0794 116.69587Q88.86551 116.38352 88.47337 116.25858Q88.09014 116.12472 87.359344 116.12472L85.60364 116.12472L85.60364 118.748436Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M145.14468 179.42029L176.00813 179.42029L176.00813 209.71367L145.14468 209.71367Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M145.14468 179.42029L176.00813 179.42029L176.00813 209.71367L145.14468 209.71367Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M162.99042 195.84207L163.9975 196.10088Q163.67667 197.34134 162.85674 198.00172Q162.03682 198.6532 160.84258 198.6532Q159.6127 198.6532 158.83734 198.15344Q158.07089 197.64476 157.66092 196.69879Q157.25987 195.7439 157.25987 194.64622Q157.25987 193.4593 157.7144 192.5758Q158.16891 191.6834 159.00667 191.22826Q159.85333 190.76419 160.8604 190.76419Q162.00116 190.76419 162.77652 191.34427Q163.5608 191.92435 163.86382 192.98633L162.87456 193.21835Q162.6072 192.3884 162.0992 192.01358Q161.60011 191.62984 160.84258 191.62984Q159.96027 191.62984 159.37207 192.04929Q158.78386 192.46872 158.54323 193.18266Q158.3026 193.88766 158.3026 194.64622Q158.3026 195.61896 158.5878 196.34183Q158.87299 197.06468 159.47011 197.43057Q160.06721 197.78755 160.76237 197.78755Q161.60902 197.78755 162.19724 197.2967Q162.78545 196.80588 162.99042 195.84207Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M56.49764 83.083466L15.717658 100.550735" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M56.49764 83.083466L15.717658 100.550735" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M56.49764 83.083466L87.36108 100.550735" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M56.49764 83.083466L87.36108 100.550735" fill-rule="evenodd"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M145.14394 100.550735L176.00739 100.550735L176.00739 130.84413L145.14394 130.84413Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M145.14394 100.550735L176.00739 100.550735L176.00739 130.84413L145.14394 130.84413Z" fill-rule="evenodd"/><clipPath id="id_1"><path d="M0 -1.4210855E-14L437.0 -1.4210855E-14L437.0 210.0L0 210.0L0 -1.4210855E-14Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_1)" d="M158.60846 119.64978L158.60846 112.019585L159.61554 112.019585L159.61554 119.64978L158.60846 119.64978ZM161.50253 119.64978L161.50253 114.84856L160.67369 114.84856L160.67369 114.125694L161.50253 114.125694L161.50253 113.5367Q161.50253 112.9834 161.60057 112.70675Q161.73425 112.34086 162.0729 112.11775Q162.41158 111.894646 163.02652 111.894646Q163.41866 111.894646 163.89992 111.98389L163.75732 112.80492Q163.47212 112.751366 163.21367 112.751366Q162.78589 112.751366 162.60765 112.938774Q162.4294 113.11726 162.4294 113.61702L162.4294 114.125694L163.50778 114.125694L163.50778 114.84856L162.4294 114.84856L162.4294 119.64978L161.50253 119.64978Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M145.14394 139.9855L176.00739 139.9855L176.00739 170.2789L145.14394 170.2789Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M145.14394 139.9855L176.00739 139.9855L176.00739 170.2789L145.14394 170.2789Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M155.06569 157.75627L155.18155 158.47021Q154.8429 158.54161 154.57553 158.54161Q154.13882 158.54161 153.8982 158.40775Q153.65756 158.26495 153.55954 158.04185Q153.4615 157.80983 153.4615 157.08696L153.4615 154.36508L152.87329 154.36508L152.87329 153.74039L153.4615 153.74039L153.4615 152.5624L154.2636 152.08049L154.2636 153.74039L155.06569 153.74039L155.06569 154.36508L154.2636 154.36508L154.2636 157.13158Q154.2636 157.47963 154.29924 157.57779Q154.34381 157.67596 154.44183 157.73843Q154.53987 157.79198 154.71812 157.79198Q154.8518 157.79198 155.06569 157.75627ZM155.8455 158.47914L155.8455 153.74039L156.5674 153.74039L156.5674 154.45432Q156.84367 153.95456 157.0754 153.79393Q157.30711 153.6333 157.5923 153.6333Q157.99335 153.6333 158.41223 153.89209L158.13594 154.63281Q157.84184 154.46324 157.54774 154.46324Q157.28929 154.46324 157.0754 154.62389Q156.87042 154.77559 156.7813 155.06117Q156.64761 155.48953 156.64761 155.99821L156.64761 158.47914L155.8455 158.47914ZM161.99493 158.47914L161.99493 157.78305Q161.44237 158.58623 160.48877 158.58623Q160.0699 158.58623 159.7045 158.4256Q159.348 158.26495 159.16977 158.024Q158.99152 157.77412 158.92023 157.42609Q158.87566 157.18513 158.87566 156.67645L158.87566 153.74039L159.67776 153.74039L159.67776 156.3641Q159.67776 156.99773 159.72232 157.2119Q159.80254 157.53317 160.04315 157.71165Q160.2927 157.89014 160.64919 157.89014Q161.01459 157.89014 161.3265 157.71165Q161.64735 157.52425 161.77213 157.20297Q161.9058 156.8817 161.9058 156.27486L161.9058 153.74039L162.7079 153.74039L162.7079 158.47914L161.99493 158.47914ZM167.20412 156.9531L168.03294 157.05127Q167.83688 157.78305 167.30214 158.18465Q166.77632 158.58623 165.9564 158.58623Q164.91368 158.58623 164.30765 157.94368Q163.70161 157.30115 163.70161 156.14992Q163.70161 154.95407 164.31656 154.29369Q164.9315 153.6333 165.91183 153.6333Q166.85654 153.6333 167.45366 154.28476Q168.05968 154.9273 168.05968 156.09637Q168.05968 156.16777 168.05968 156.31056L164.53046 156.31056Q164.57501 157.09589 164.96715 157.51532Q165.3682 157.92584 165.9564 157.92584Q166.40201 157.92584 166.71394 157.6938Q167.02586 157.46178 167.20412 156.9531ZM164.57501 155.65016L167.21303 155.65016Q167.15955 155.05225 166.91 154.75775Q166.52678 154.29369 165.92076 154.29369Q165.3682 154.29369 164.98497 154.6685Q164.61066 155.0344 164.57501 155.65016Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M56.49764 83.083466L160.56743 100.550735" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M56.49764 83.083466L160.56743 100.550735" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M160.57567 130.84413L160.57567 139.98251" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M160.57567 130.84413L160.57567 139.98251" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M160.57567 170.2789L160.57567 179.41728" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M160.57567 170.2789L160.57567 179.41728" fill-rule="evenodd"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M83.89291 59.20613L101.96544 59.20613L101.96544 76.6734L83.89291 76.6734Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M83.89291 59.20613L101.96544 59.20613L101.96544 76.6734L83.89291 76.6734Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M93.330925 71.213875Q92.8051 71.65116 92.323845 71.83857Q91.8515 72.01705 91.29894 72.01705Q90.3899 72.01705 89.89973 71.57085Q89.40955 71.12463 89.40955 70.43747Q89.40955 70.026955 89.59671 69.69676Q89.78387 69.35764 90.07797 69.16131Q90.38098 68.956055 90.755295 68.84896Q91.02267 68.777565 91.58413 68.70618Q92.71598 68.57231 93.25071 68.3849Q93.25963 68.18857 93.25963 68.14395Q93.25963 67.5728 92.992256 67.33185Q92.63577 67.0195 91.9228 67.0195Q91.26329 67.0195 90.95136 67.251526Q90.639435 67.48356 90.48793 68.06363L89.56998 67.93869Q89.69475 67.35862 89.979935 67.00165Q90.26513 66.63576 90.80877 66.43943Q91.35242 66.243095 92.05648 66.243095Q92.769455 66.243095 93.206154 66.41265Q93.651764 66.57329 93.85674 66.83209Q94.07063 67.08197 94.15085 67.46571Q94.195404 67.706665 94.195404 68.33136L94.195404 69.58075Q94.195404 70.88368 94.25779 71.23173Q94.320175 71.57085 94.48951 71.89211L93.51808 71.89211Q93.36657 71.59762 93.330925 71.213875ZM93.25071 69.11669Q92.742714 69.33087 91.72673 69.473656Q91.14743 69.55398 90.90681 69.661064Q90.666176 69.76816 90.53249 69.964485Q90.40772 70.16082 90.40772 70.4107Q90.40772 70.785515 90.69291 71.03539Q90.9781 71.28527 91.52174 71.28527Q92.05648 71.28527 92.47535 71.05324Q92.90314 70.81229 93.099205 70.40177Q93.25071 70.080505 93.25071 69.46473L93.25071 69.11669Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M71.92936 67.93677L83.89388 67.93677" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M71.92936 67.93677L83.89388 67.93677" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M44.06258 106.954796L62.13512 106.954796L62.13512 124.422066L44.06258 124.422066Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M44.06258 106.954796L62.13512 106.954796L62.13512 124.422066L44.06258 124.422066Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M50.755642 119.640785L49.89116 119.640785L49.89116 112.01059L50.826942 112.01059L50.826942 114.732475Q51.424057 113.99176 52.342014 113.99176Q52.85001 113.99176 53.30453 114.19702Q53.759056 114.402275 54.053158 114.77709Q54.34726 115.14298 54.50768 115.66951Q54.677013 116.196045 54.677013 116.79396Q54.677013 118.21291 53.972946 118.98932Q53.277798 119.765724 52.288544 119.765724Q51.317112 119.765724 50.755642 118.944695L50.755642 119.640785ZM50.74673 116.838585Q50.74673 117.82917 51.02301 118.27538Q51.45971 118.998245 52.217243 118.998245Q52.832188 118.998245 53.277798 118.46279Q53.723408 117.92734 53.723408 116.87428Q53.723408 115.79445 53.29562 115.27685Q52.867836 114.75925 52.25289 114.75925Q51.646862 114.75925 51.19234 115.2947Q50.74673 115.83015 50.74673 116.838585Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M31.149378 115.68843L44.066032 115.68843" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M31.149378 115.68843L44.066032 115.68843" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M114.93319 106.96379L133.00572 106.96379L133.00572 124.43106L114.93319 124.43106Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M114.93319 106.96379L133.00572 106.96379L133.00572 124.43106L114.93319 124.43106Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M121.62625 119.64978L120.76177 119.64978L120.76177 112.019585L121.69755 112.019585L121.69755 114.74146Q122.29467 114.000755 123.21262 114.000755Q123.72062 114.000755 124.17514 114.20602Q124.62966 114.41127 124.92377 114.78609Q125.21787 115.15198 125.37829 115.678505Q125.54762 116.20503 125.54762 116.802956Q125.54762 118.22191 124.84356 118.998314Q124.14841 119.77472 123.15915 119.77472Q122.18772 119.77472 121.62625 118.95369L121.62625 119.64978ZM121.61734 116.84758Q121.61734 117.838165 121.893616 118.28438Q122.330315 119.00724 123.08785 119.00724Q123.7028 119.00724 124.14841 118.47179Q124.59402 117.93633 124.59402 116.88328Q124.59402 115.803444 124.16623 115.28584Q123.73844 114.76824 123.123505 114.76824Q122.51747 114.76824 122.06295 115.30369Q121.61734 115.83914 121.61734 116.84758Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M102.79279 115.697426L114.936966 115.697426" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M102.79279 115.697426L114.936966 115.697426" fill-rule="evenodd"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M186.57303 185.83334L204.64557 185.83334L204.64557 203.30061L186.57303 203.30061Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M186.57303 185.83334L204.64557 185.83334L204.64557 203.30061L186.57303 203.30061Z" fill-rule="evenodd"/><clipPath id="id_2"><path d="M0 -2.842171E-14L437.0 -2.842171E-14L437.0 210.0L0 210.0L0 -2.842171E-14Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_2)" d="M196.01105 196.49353L196.929 196.61847Q196.7775 197.56444 196.15364 198.10881Q195.5387 198.64427 194.62965 198.64427Q193.4978 198.64427 192.81157 197.90356Q192.12534 197.16286 192.12534 195.7796Q192.12534 194.87825 192.41943 194.20894Q192.71353 193.53963 193.31956 193.20943Q193.9256 192.87032 194.63857 192.87032Q195.5387 192.87032 196.10909 193.32544Q196.67946 193.78058 196.83989 194.61945L195.93083 194.75331Q195.79715 194.20001 195.4674 193.92337Q195.13765 193.63779 194.67422 193.63779Q193.97015 193.63779 193.52455 194.14647Q193.07893 194.65515 193.07893 195.75282Q193.07893 196.86835 193.50671 197.37703Q193.93451 197.87679 194.62074 197.87679Q195.1733 197.87679 195.5387 197.53767Q195.91301 197.19855 196.01105 196.49353Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M176.00813 194.56699L186.5714 194.56699" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M176.00813 194.56699L186.5714 194.56699" fill-rule="evenodd"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M273.13455 52.790073L303.998 52.790073L303.998 83.083466L273.13455 83.083466Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M273.13455 52.790073L303.998 52.790073L303.998 83.083466L273.13455 83.083466Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M284.99966 71.889114L287.92285 64.25892L289.01013 64.25892L292.12943 71.889114L290.97974 71.889114L290.09744 69.57775L286.90686 69.57775L286.06912 71.889114L284.99966 71.889114ZM287.20096 68.75672L289.7855 68.75672L288.9834 66.641685Q288.62692 65.67787 288.44867 65.0621Q288.29718 65.793884 288.0387 66.51675L287.20096 68.75672Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M232.35457 100.54174L263.21802 100.54174L263.21802 130.83513L232.35457 130.83513Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M232.35457 100.54174L263.21802 100.54174L263.21802 130.83513L232.35457 130.83513Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M245.01286 119.640785L245.01286 112.01059L247.87367 112.01059Q248.74707 112.01059 249.27289 112.242615Q249.7987 112.47465 250.0928 112.95656Q250.39583 113.43846 250.39583 113.96499Q250.39583 114.45582 250.12846 114.893105Q249.87001 115.32147 249.33528 115.58027Q250.02151 115.78553 250.39583 116.27636Q250.77014 116.76719 250.77014 117.42758Q250.77014 117.963036 250.53842 118.42709Q250.31561 118.882225 249.97696 119.1321Q249.6472 119.38198 249.14812 119.51585Q248.64903 119.640785 247.91823 119.640785L245.01286 119.640785ZM246.02885 115.21438L247.66869 115.21438Q248.34602 115.21438 248.63121 115.13406Q249.01443 115.01804 249.21051 114.75032Q249.40657 114.4826 249.40657 114.081Q249.40657 113.706184 249.21942 113.420616Q249.04117 113.126114 248.70251 113.01903Q248.36385 112.911934 247.54391 112.911934L246.02885 112.911934L246.02885 115.21438ZM246.02885 118.73944L247.91823 118.73944Q248.4084 118.73944 248.60448 118.70374Q248.95206 118.64127 249.18376 118.49849Q249.4244 118.34677 249.5759 118.07012Q249.72742 117.79347 249.72742 117.42758Q249.72742 116.99922 249.50461 116.686874Q249.29071 116.37453 248.89857 116.24959Q248.51535 116.11572 247.78455 116.11572L246.02885 116.11572L246.02885 118.73944Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M303.998 100.550735L334.86142 100.550735L334.86142 130.84413L303.998 130.84413Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M303.998 100.550735L334.86142 100.550735L334.86142 130.84413L303.998 130.84413Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_1)" d="M316.65628 119.64978L316.65628 112.019585L319.5171 112.019585Q320.39047 112.019585 320.91632 112.25161Q321.44214 112.48364 321.73624 112.965546Q322.03925 113.44746 322.03925 113.97398Q322.03925 114.46481 321.77188 114.9021Q321.51343 115.33047 320.9787 115.589264Q321.66492 115.794525 322.03925 116.285355Q322.41354 116.776184 322.41354 117.43658Q322.41354 117.97203 322.18185 118.43609Q321.95905 118.89122 321.62036 119.1411Q321.29062 119.390976 320.79153 119.52484Q320.29245 119.64978 319.56165 119.64978L316.65628 119.64978ZM317.67227 115.22337L319.3121 115.22337Q319.98944 115.22337 320.27463 115.14306Q320.65787 115.02704 320.8539 114.759315Q321.05 114.491585 321.05 114.09Q321.05 113.71518 320.86282 113.42961Q320.6846 113.13511 320.34592 113.028015Q320.00726 112.92093 319.18735 112.92093L317.67227 112.92093L317.67227 115.22337ZM317.67227 118.748436L319.56165 118.748436Q320.05182 118.748436 320.2479 118.71274Q320.59546 118.65027 320.82718 118.507484Q321.0678 118.35577 321.21933 118.07912Q321.37082 117.80247 321.37082 117.43658Q321.37082 117.00822 321.148 116.69587Q320.93414 116.38352 320.542 116.25858Q320.15878 116.12472 319.42798 116.12472L317.67227 116.12472L317.67227 118.748436Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M377.21332 179.42029L408.07675 179.42029L408.07675 209.71367L377.21332 209.71367Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M377.21332 179.42029L408.07675 179.42029L408.07675 209.71367L377.21332 209.71367Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M389.6226 198.51933L389.6226 190.88913L392.2517 190.88913Q393.134 190.88913 393.60635 191.00514Q394.25696 191.14793 394.72037 191.54953Q395.32642 192.05821 395.6205 192.85246Q395.92352 193.64671 395.92352 194.66408Q395.92352 195.52972 395.71854 196.20796Q395.51358 196.87727 395.19272 197.31456Q394.8808 197.75185 394.5065 198.01065Q394.13217 198.26053 393.59744 198.3944Q393.0627 198.51933 392.37646 198.51933L389.6226 198.51933ZM390.62967 197.61798L392.26062 197.61798Q393.00925 197.61798 393.437 197.48412Q393.8648 197.34134 394.12326 197.08253Q394.47977 196.72557 394.6758 196.11871Q394.8808 195.51187 394.8808 194.64622Q394.8808 193.45038 394.48868 192.80785Q394.09653 192.1653 393.53506 191.95111Q393.134 191.79048 392.2339 191.79048L390.62967 191.79048L390.62967 197.61798Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M288.56625 83.083466L247.78629 100.550735" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M288.56625 83.083466L247.78629 100.550735" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M288.56625 83.083466L319.4297 100.550735" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M288.56625 83.083466L319.4297 100.550735" fill-rule="evenodd"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M377.21255 100.550735L408.07602 100.550735L408.07602 130.84413L377.21255 130.84413Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M377.21255 100.550735L408.07602 100.550735L408.07602 130.84413L377.21255 130.84413Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_1)" d="M390.6771 119.64978L390.6771 112.019585L391.68417 112.019585L391.68417 119.64978L390.6771 119.64978ZM393.57117 119.64978L393.57117 114.84856L392.7423 114.84856L392.7423 114.125694L393.57117 114.125694L393.57117 113.5367Q393.57117 112.9834 393.6692 112.70675Q393.8029 112.34086 394.14154 112.11775Q394.4802 111.894646 395.09515 111.894646Q395.48727 111.894646 395.96854 111.98389L395.82593 112.80492Q395.54074 112.751366 395.2823 112.751366Q394.85452 112.751366 394.67627 112.938774Q394.49802 113.11726 394.49802 113.61702L394.49802 114.125694L395.5764 114.125694L395.5764 114.84856L394.49802 114.84856L394.49802 119.64978L393.57117 119.64978Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M377.21255 139.9855L408.07602 139.9855L408.07602 170.2789L377.21255 170.2789Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M377.21255 139.9855L408.07602 139.9855L408.07602 170.2789L377.21255 170.2789Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M383.79892 158.47914L383.79892 154.36508L383.09488 154.36508L383.09488 153.74039L383.79892 153.74039L383.79892 153.2317Q383.79892 152.75873 383.88806 152.52669Q384.0039 152.21434 384.2891 152.01802Q384.5832 151.82169 385.109 151.82169Q385.4477 151.82169 385.85764 151.902L385.7418 152.60701Q385.49225 152.5624 385.26944 152.5624Q384.90405 152.5624 384.75253 152.72302Q384.601 152.87474 384.601 153.3031L384.601 153.74039L385.51898 153.74039L385.51898 154.36508L384.601 154.36508L384.601 158.47914L383.79892 158.47914ZM389.23093 157.89014Q388.7853 158.27388 388.37534 158.43452Q387.9654 158.58623 387.49304 158.58623Q386.70877 158.58623 386.2899 158.20248Q385.871 157.81874 385.871 157.22975Q385.871 156.8817 386.03143 156.59613Q386.19186 156.30164 386.4414 156.13206Q386.69986 155.95358 387.0207 155.86435Q387.26132 155.80188 387.73367 155.74834Q388.7051 155.63232 389.16852 155.47168Q389.16852 155.30212 389.16852 155.2575Q389.16852 154.76666 388.9457 154.57034Q388.6338 154.29369 388.02777 154.29369Q387.4663 154.29369 387.19003 154.49895Q386.92267 154.69528 386.79788 155.19504L386.0136 155.08794Q386.12054 154.58818 386.36118 154.27583Q386.61072 153.96349 387.07416 153.80286Q387.5376 153.6333 388.14362 153.6333Q388.74966 153.6333 389.12396 153.77608Q389.5072 153.91887 389.68542 154.13306Q389.86368 154.34723 389.93497 154.67743Q389.97955 154.88269 389.97955 155.41814L389.97955 156.48904Q389.97955 157.6135 390.0241 157.90799Q390.07758 158.20248 390.2291 158.47914L389.39133 158.47914Q389.26657 158.22926 389.23093 157.89014ZM389.16852 156.09637Q388.73184 156.27486 387.85843 156.3998Q387.35934 156.47119 387.15436 156.56044Q386.9494 156.64967 386.83353 156.82816Q386.7266 156.99773 386.7266 157.20297Q386.7266 157.52425 386.96722 157.73843Q387.21677 157.9526 387.6802 157.9526Q388.14362 157.9526 388.50012 157.75627Q388.8655 157.55103 389.03485 157.19405Q389.16852 156.92633 389.16852 156.39087L389.16852 156.09637ZM391.20496 158.47914L391.20496 151.9377L392.00705 151.9377L392.00705 158.47914L391.20496 158.47914ZM392.92947 157.0602L393.72266 156.93526Q393.78503 157.41716 394.08807 157.67596Q394.4 157.92584 394.94363 157.92584Q395.4962 157.92584 395.76355 157.70273Q396.0309 157.4707 396.0309 157.16728Q396.0309 156.89955 395.7992 156.73892Q395.62988 156.63182 394.97928 156.47119Q394.097 156.2481 393.7494 156.08745Q393.41074 155.9179 393.23248 155.63232Q393.06317 155.34674 393.06317 154.9987Q393.06317 154.67743 393.20575 154.4097Q393.34836 154.14197 393.5979 153.96349Q393.78503 153.82071 394.1059 153.73146Q394.43564 153.6333 394.80103 153.6333Q395.36252 153.6333 395.78137 153.79393Q396.20026 153.95456 396.39633 154.23122Q396.60132 154.49895 396.68152 154.963L395.89725 155.0701Q395.84378 154.7042 395.58533 154.49895Q395.32687 154.29369 394.86343 154.29369Q394.31088 154.29369 394.07025 154.4811Q393.83853 154.65958 393.83853 154.90053Q393.83853 155.06117 393.93655 155.18611Q394.03458 155.31105 394.23956 155.40028Q394.36435 155.44492 394.95255 155.60555Q395.7992 155.82864 396.13788 155.98036Q396.47653 156.12315 396.6637 156.3998Q396.85977 156.67645 396.85977 157.08696Q396.85977 157.48856 396.61914 157.84552Q396.38742 158.19356 395.9418 158.3899Q395.5051 158.58623 394.95255 158.58623Q394.02567 158.58623 393.54443 158.20248Q393.06317 157.81874 392.92947 157.0602ZM401.0485 156.9531L401.87732 157.05127Q401.68124 157.78305 401.1465 158.18465Q400.6207 158.58623 399.80078 158.58623Q398.75806 158.58623 398.15204 157.94368Q397.546 157.30115 397.546 156.14992Q397.546 154.95407 398.16095 154.29369Q398.77588 153.6333 399.75623 153.6333Q400.70093 153.6333 401.29803 154.28476Q401.90405 154.9273 401.90405 156.09637Q401.90405 156.16777 401.90405 156.31056L398.37482 156.31056Q398.4194 157.09589 398.81152 157.51532Q399.2126 157.92584 399.80078 157.92584Q400.2464 157.92584 400.55832 157.6938Q400.87024 157.46178 401.0485 156.9531ZM398.4194 155.65016L401.0574 155.65016Q401.00394 155.05225 400.7544 154.75775Q400.37115 154.29369 399.76514 154.29369Q399.2126 154.29369 398.82935 154.6685Q398.45505 155.0344 398.4194 155.65016Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M288.56625 83.083466L392.63605 100.550735" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M288.56625 83.083466L392.63605 100.550735" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M392.6443 130.84413L392.6443 139.98251" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M392.6443 130.84413L392.6443 139.98251" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M392.6443 170.2789L392.6443 179.41728" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M392.6443 170.2789L392.6443 179.41728" fill-rule="evenodd"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M315.96152 59.20613L334.03406 59.20613L334.03406 76.6734L315.96152 76.6734Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M315.96152 59.20613L334.03406 59.20613L334.03406 76.6734L315.96152 76.6734Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M325.39954 71.213875Q324.87372 71.65116 324.39246 71.83857Q323.92014 72.01705 323.36755 72.01705Q322.45853 72.01705 321.96835 71.57085Q321.47818 71.12463 321.47818 70.43747Q321.47818 70.026955 321.66534 69.69676Q321.85248 69.35764 322.1466 69.16131Q322.44962 68.956055 322.8239 68.84896Q323.09128 68.777565 323.65277 68.70618Q324.7846 68.57231 325.31934 68.3849Q325.32825 68.18857 325.32825 68.14395Q325.32825 67.5728 325.06088 67.33185Q324.7044 67.0195 323.99142 67.0195Q323.3319 67.0195 323.02 67.251526Q322.70807 67.48356 322.55655 68.06363L321.6386 67.93869Q321.76337 67.35862 322.04855 67.00165Q322.33374 66.63576 322.87738 66.43943Q323.42105 66.243095 324.1251 66.243095Q324.83807 66.243095 325.27478 66.41265Q325.7204 66.57329 325.92535 66.83209Q326.13925 67.08197 326.21948 67.46571Q326.26404 67.706665 326.26404 68.33136L326.26404 69.58075Q326.26404 70.88368 326.32642 71.23173Q326.3888 71.57085 326.55814 71.89211L325.5867 71.89211Q325.4352 71.59762 325.39954 71.213875ZM325.31934 69.11669Q324.81134 69.33087 323.79535 69.473656Q323.21606 69.55398 322.97543 69.661064Q322.7348 69.76816 322.6011 69.964485Q322.47635 70.16082 322.47635 70.4107Q322.47635 70.785515 322.76154 71.03539Q323.04672 71.28527 323.59036 71.28527Q324.1251 71.28527 324.54398 71.05324Q324.97177 70.81229 325.16782 70.40177Q325.31934 70.080505 325.31934 69.46473L325.31934 69.11669Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M303.998 67.93677L315.9625 67.93677" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M303.998 67.93677L315.9625 67.93677" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M276.1312 106.954796L294.20374 106.954796L294.20374 124.422066L276.1312 124.422066Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M276.1312 106.954796L294.20374 106.954796L294.20374 124.422066L276.1312 124.422066Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M282.82428 119.640785L281.95978 119.640785L281.95978 112.01059L282.89557 112.01059L282.89557 114.732475Q283.49268 113.99176 284.41064 113.99176Q284.91864 113.99176 285.37317 114.19702Q285.82767 114.402275 286.1218 114.77709Q286.4159 115.14298 286.5763 115.66951Q286.74564 116.196045 286.74564 116.79396Q286.74564 118.21291 286.04156 118.98932Q285.34644 119.765724 284.35718 119.765724Q283.38574 119.765724 282.82428 118.944695L282.82428 119.640785ZM282.81537 116.838585Q282.81537 117.82917 283.09164 118.27538Q283.52832 118.998245 284.28586 118.998245Q284.90082 118.998245 285.34644 118.46279Q285.79202 117.92734 285.79202 116.87428Q285.79202 115.79445 285.36426 115.27685Q284.93646 114.75925 284.32153 114.75925Q283.71548 114.75925 283.26096 115.2947Q282.81537 115.83015 282.81537 116.838585Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M263.21802 115.68843L276.13464 115.68843" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M263.21802 115.68843L276.13464 115.68843" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M347.0018 106.96379L365.07434 106.96379L365.07434 124.43106L347.0018 124.43106Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M347.0018 106.96379L365.07434 106.96379L365.07434 124.43106L347.0018 124.43106Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M353.6949 119.64978L352.83038 119.64978L352.83038 112.019585L353.76617 112.019585L353.76617 114.74146Q354.36328 114.000755 355.28125 114.000755Q355.78925 114.000755 356.24377 114.20602Q356.6983 114.41127 356.9924 114.78609Q357.2865 115.15198 357.4469 115.678505Q357.61624 116.20503 357.61624 116.802956Q357.61624 118.22191 356.91217 118.998314Q356.21704 119.77472 355.22778 119.77472Q354.25635 119.77472 353.6949 118.95369L353.6949 119.64978ZM353.68597 116.84758Q353.68597 117.838165 353.96225 118.28438Q354.39896 119.00724 355.15646 119.00724Q355.77142 119.00724 356.21704 118.47179Q356.66263 117.93633 356.66263 116.88328Q356.66263 115.803444 356.23486 115.28584Q355.80707 114.76824 355.19214 114.76824Q354.5861 114.76824 354.13156 115.30369Q353.68597 115.83914 353.68597 116.84758Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M334.86142 115.697426L347.00558 115.697426" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M334.86142 115.697426L347.00558 115.697426" fill-rule="evenodd"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M418.64166 185.83334L436.7142 185.83334L436.7142 203.30061L418.64166 203.30061Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M418.64166 185.83334L436.7142 185.83334L436.7142 203.30061L418.64166 203.30061Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_2)" d="M428.05295 198.51933L428.05295 197.82324Q427.53604 198.64427 426.51114 198.64427Q425.85162 198.64427 425.29907 198.27838Q424.74652 197.91249 424.44348 197.26102Q424.14047 196.60956 424.14047 195.76175Q424.14047 194.9318 424.41675 194.26248Q424.69302 193.58424 425.2367 193.22728Q425.78925 192.87032 426.47546 192.87032Q426.97455 192.87032 427.3578 193.08449Q427.7499 193.28975 427.99054 193.62886L427.99054 190.88913L428.92633 190.88913L428.92633 198.51933L428.05295 198.51933ZM425.103 195.76175Q425.103 196.82373 425.5486 197.35027Q425.99423 197.87679 426.60025 197.87679Q427.21518 197.87679 427.64297 197.37703Q428.07077 196.86835 428.07077 195.84207Q428.07077 194.7087 427.63406 194.18217Q427.19736 193.64671 426.5557 193.64671Q425.94073 193.64671 425.52188 194.1554Q425.103 194.66408 425.103 195.76175Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M408.07675 194.56699L418.64 194.56699" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5707648336006036,1.7122945008018107" clip-path="url(#id_0)" d="M408.07675 194.56699L418.64 194.56699" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M181.28976 30.579716L288.57526 52.796066" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5707648336006036" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M181.28976 30.579716L288.57526 52.796066" fill-rule="evenodd"/></svg>
\ No newline at end of file
diff --git a/compose/runtime/design/images/call-tree-3.svg b/compose/runtime/design/images/call-tree-3.svg
new file mode 100644
index 0000000..c09aeb9
--- /dev/null
+++ b/compose/runtime/design/images/call-tree-3.svg
@@ -0,0 +1 @@
+<svg version="1.1" viewBox="0.0 0.0 583.0 130.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" preserveAspectRatio="none" width="583" height="130" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="id_0"><path d="M0 0L583.0 0L583.0 130.0L0 130.0L0 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M-3.492981 -7.366634L948.159 -7.366634L948.159 706.9547L-3.492981 706.9547Z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M291.5 49.119358L181.79308 77.75783" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M291.5 49.119358L181.79308 77.75783" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M291.5 49.119358L389.803 77.75783" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M291.5 49.119358L389.803 77.75783" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M0.37271473 0.37301886L582.62726 0.37301886L582.62726 49.119358L0.37271473 49.119358Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M0.37271473 0.37301886L582.62726 0.37301886L582.62726 49.119358L0.37271473 49.119358Z" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M6.1488175 5.0152483L91.19803 5.0152483L91.19803 44.481033L6.1488175 44.481033Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M6.1488175 5.0152483L91.19803 5.0152483L91.19803 44.481033L6.1488175 44.481033Z" fill-rule="evenodd"/><clipPath id="id_1"><path d="M8.881784E-16 0L583.0 0L583.0 130.0L8.881784E-16 130.0L8.881784E-16 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_1)" d="M35.11603 29.897205L35.11603 19.956697L39.518814 19.956697Q40.843136 19.956697 41.52853 20.224104Q42.22554 20.491508 42.63213 21.177462Q43.050335 21.85179 43.050335 22.677258Q43.050335 23.723627 42.36494 24.456085Q41.679546 25.176918 40.250675 25.374565Q40.773434 25.630344 41.05224 25.874496Q41.621464 26.397682 42.132607 27.18827L43.863514 29.897205L42.213924 29.897205L40.90122 27.827719Q40.320377 26.932493 39.94864 26.46744Q39.5769 25.99076 39.27486 25.804739Q38.98444 25.607092 38.682404 25.537334Q38.46168 25.479202 37.950542 25.479202L36.428734 25.479202L36.428734 29.897205L35.11603 29.897205ZM36.428734 24.351448L39.25163 24.351448Q40.15774 24.351448 40.657265 24.165428Q41.168407 23.96778 41.435593 23.560858Q41.70278 23.153936 41.70278 22.677258Q41.70278 21.968052 41.19164 21.514626Q40.6805 21.0612 39.56528 21.0612L36.428734 21.0612L36.428734 24.351448ZM44.50556 26.293045Q44.50556 24.293318 45.60916 23.339958Q46.53851 22.537743 47.874447 22.537743Q49.3614 22.537743 50.30237 23.514353Q51.243332 24.479338 51.243332 26.200033Q51.243332 27.583567 50.825127 28.385782Q50.406918 29.176373 49.605354 29.618174Q48.81541 30.059973 47.874447 30.059973Q46.364258 30.059973 45.43491 29.094988Q44.50556 28.118378 44.50556 26.293045ZM45.76018 26.293045Q45.76018 27.676577 46.35264 28.374157Q46.956715 29.06011 47.874447 29.06011Q48.78056 29.06011 49.384636 28.374157Q49.988712 27.676577 49.988712 26.258165Q49.988712 24.921139 49.384636 24.235186Q48.78056 23.549232 47.874447 23.549232Q46.956715 23.549232 46.35264 24.235186Q45.76018 24.909512 45.76018 26.293045ZM52.21968 26.293045Q52.21968 24.293318 53.32328 23.339958Q54.25263 22.537743 55.588566 22.537743Q57.075523 22.537743 58.016487 23.514353Q58.95745 24.479338 58.95745 26.200033Q58.95745 27.583567 58.539246 28.385782Q58.12104 29.176373 57.319477 29.618174Q56.529533 30.059973 55.588566 30.059973Q54.078377 30.059973 53.14903 29.094988Q52.21968 28.118378 52.21968 26.293045ZM53.4743 26.293045Q53.4743 27.676577 54.06676 28.374157Q54.670837 29.06011 55.588566 29.06011Q56.494682 29.06011 57.09876 28.374157Q57.70283 27.676577 57.70283 26.258165Q57.70283 24.921139 57.09876 24.235186Q56.494682 23.549232 55.588566 23.549232Q54.670837 23.549232 54.06676 24.235186Q53.4743 24.909512 53.4743 26.293045ZM63.047115 28.80433L63.221367 29.885578Q62.710228 29.990215 62.30364 29.990215Q61.641476 29.990215 61.269737 29.780941Q60.909615 29.571669 60.7586 29.234505Q60.60758 28.885715 60.60758 27.792841L60.60758 23.642242L59.71308 23.642242L59.71308 22.70051L60.60758 22.70051L60.60758 20.921682L61.81573 20.189224L61.81573 22.70051L63.047115 22.70051L63.047115 23.642242L61.81573 23.642242L61.81573 27.862598Q61.81573 28.374157 61.873814 28.5253Q61.943516 28.676441 62.082916 28.769451Q62.233936 28.850836 62.51274 28.850836Q62.710228 28.850836 63.047115 28.80433Z" fill-rule="nonzero"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M99.53435 5.0152483L139.76413 5.0152483L139.76413 44.481033L99.53435 44.481033Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M99.53435 5.0152483L139.76413 5.0152483L139.76413 44.481033L99.53435 44.481033Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M115.000244 29.897205L118.81056 19.956697L120.22782 19.956697L124.29372 29.897205L122.79514 29.897205L121.64507 26.885986L117.486244 26.885986L116.394264 29.897205L115.000244 29.897205ZM117.8696 25.816366L121.23849 25.816366L120.19297 23.060926Q119.728294 21.805285 119.49596 21.003067Q119.29847 21.956427 118.961586 22.898157L117.8696 25.816366Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M179.9939 5.013295L220.22368 5.013295L220.22368 44.47908L179.9939 44.47908Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M179.9939 5.013295L220.22368 5.013295L220.22368 44.47908L179.9939 44.47908Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M196.49368 29.895252L196.49368 19.954744L200.2227 19.954744Q201.36115 19.954744 202.04654 20.257029Q202.73193 20.559315 203.1153 21.187136Q203.51027 21.814957 203.51027 22.50091Q203.51027 23.140358 203.16176 23.710047Q202.82487 24.268112 202.12785 24.605274Q203.02235 24.87268 203.51027 25.512129Q203.99817 26.151575 203.99817 27.011923Q203.99817 27.709503 203.69614 28.314072Q203.40572 28.907015 202.96428 29.232552Q202.53445 29.558088 201.88391 29.732483Q201.23337 29.895252 200.28078 29.895252L196.49368 29.895252ZM197.81801 24.128595L199.9555 24.128595Q200.8384 24.128595 201.21013 24.023958Q201.70966 23.872816 201.96523 23.524027Q202.2208 23.175238 202.2208 22.652052Q202.2208 22.163746 201.97684 21.791704Q201.7445 21.408035 201.30307 21.26852Q200.86162 21.129004 199.79288 21.129004L197.81801 21.129004L197.81801 24.128595ZM197.81801 28.720993L200.28078 28.720993Q200.91971 28.720993 201.17528 28.674488Q201.62834 28.593103 201.93037 28.407082Q202.24403 28.209435 202.44151 27.849018Q202.639 27.488604 202.639 27.011923Q202.639 26.45386 202.34859 26.046938Q202.06978 25.640017 201.55864 25.47725Q201.05911 25.302855 200.10652 25.302855L197.81801 25.302855L197.81801 28.720993Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M260.45346 5.013295L300.68323 5.013295L300.68323 44.47908L260.45346 44.47908Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M260.45346 5.013295L300.68323 5.013295L300.68323 44.47908L260.45346 44.47908Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M276.95325 29.895252L276.95325 19.954744L280.68225 19.954744Q281.8207 19.954744 282.5061 20.257029Q283.1915 20.559315 283.57483 21.187136Q283.96982 21.814957 283.96982 22.50091Q283.96982 23.140358 283.6213 23.710047Q283.28442 24.268112 282.5874 24.605274Q283.4819 24.87268 283.96982 25.512129Q284.45773 26.151575 284.45773 27.011923Q284.45773 27.709503 284.15567 28.314072Q283.86526 28.907015 283.42383 29.232552Q282.994 29.558088 282.34344 29.732483Q281.6929 29.895252 280.74033 29.895252L276.95325 29.895252ZM278.27756 24.128595L280.41507 24.128595Q281.29794 24.128595 281.66968 24.023958Q282.1692 23.872816 282.42477 23.524027Q282.68036 23.175238 282.68036 22.652052Q282.68036 22.163746 282.4364 21.791704Q282.20404 21.408035 281.7626 21.26852Q281.32117 21.129004 280.2524 21.129004L278.27756 21.129004L278.27756 24.128595ZM278.27756 28.720993L280.74033 28.720993Q281.37927 28.720993 281.63483 28.674488Q282.0879 28.593103 282.38992 28.407082Q282.70358 28.209435 282.90106 27.849018Q283.09854 27.488604 283.09854 27.011923Q283.09854 26.45386 282.80814 26.046938Q282.52933 25.640017 282.0182 25.47725Q281.51865 25.302855 280.56607 25.302855L278.27756 25.302855L278.27756 28.720993Z" fill-rule="nonzero"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M148.10112 13.37009L171.65825 13.37009L171.65825 36.126194L148.10112 36.126194Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7437813458751885,2.2313440376255658" clip-path="url(#id_0)" d="M148.10112 13.37009L171.65825 13.37009L171.65825 36.126194L148.10112 36.126194Z" fill-rule="evenodd"/><clipPath id="id_2"><path d="M0 1.7763568E-15L583.0 1.7763568E-15L583.0 130.0L0 130.0L0 1.7763568E-15Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_2)" d="M160.40337 29.013605Q159.71797 29.583294 159.09065 29.827448Q158.47496 30.059975 157.75471 30.059975Q156.56981 30.059975 155.93088 29.478659Q155.29195 28.89734 155.29195 28.002115Q155.29195 27.467304 155.5359 27.03713Q155.77986 26.59533 156.16321 26.33955Q156.55818 26.072145 157.0461 25.932629Q157.39459 25.839619 158.12646 25.746609Q159.6018 25.572212 160.29881 25.32806Q160.31042 25.07228 160.31042 25.014149Q160.31042 24.270065 159.96193 23.956154Q159.49725 23.549232 158.5679 23.549232Q157.70825 23.549232 157.30167 23.851517Q156.89508 24.153801 156.69759 24.909512L155.50105 24.746744Q155.6637 23.991034 156.03543 23.52598Q156.40717 23.049301 157.1158 22.793522Q157.82442 22.537743 158.74216 22.537743Q159.6715 22.537743 160.24072 22.758642Q160.82156 22.967916 161.08876 23.30508Q161.36755 23.630617 161.4721 24.130548Q161.5302 24.44446 161.5302 25.258303L161.5302 26.885988Q161.5302 28.583431 161.61151 29.036858Q161.69283 29.478659 161.91356 29.897205L160.64731 29.897205Q160.44983 29.513536 160.40337 29.013605ZM160.29881 26.281418Q159.63666 26.56045 158.31233 26.746471Q157.55724 26.851109 157.24358 26.990625Q156.92993 27.13014 156.75568 27.38592Q156.59303 27.641699 156.59303 27.967236Q156.59303 28.455542 156.96478 28.781078Q157.33652 29.106615 158.04514 29.106615Q158.74216 29.106615 159.28815 28.80433Q159.84575 28.49042 160.10132 27.95561Q160.29881 27.537062 160.29881 26.734846L160.29881 26.281418Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M228.56067 13.37009L252.1178 13.37009L252.1178 36.126194L228.56067 36.126194Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7437813458751885,2.2313440376255658" clip-path="url(#id_0)" d="M228.56067 13.37009L252.1178 13.37009L252.1178 36.126194L228.56067 36.126194Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_2)" d="M237.28493 29.897205L236.1581 29.897205L236.1581 19.9567L237.37785 19.9567L237.37785 23.502728Q238.15619 22.537743 239.35272 22.537743Q240.01488 22.537743 240.60735 22.805147Q241.1998 23.072554 241.58316 23.56086Q241.9665 24.037539 242.17561 24.723492Q242.39633 25.409445 242.39633 26.188408Q242.39633 28.036993 241.4786 29.048483Q240.5725 30.059975 239.28302 30.059975Q238.01678 30.059975 237.28493 28.990353L237.28493 29.897205ZM237.2733 26.24654Q237.2733 27.537062 237.63342 28.118378Q238.20265 29.06011 239.19008 29.06011Q239.99165 29.06011 240.5725 28.36253Q241.15334 27.664951 241.15334 26.293045Q241.15334 24.88626 240.59572 24.211933Q240.03812 23.537607 239.23656 23.537607Q238.44661 23.537607 237.85416 24.235186Q237.2733 24.932766 237.2733 26.24654Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M309.02023 13.368136L332.57736 13.368136L332.57736 36.12424L309.02023 36.12424Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7437813458751885,2.2313440376255658" clip-path="url(#id_0)" d="M309.02023 13.368136L332.57736 13.368136L332.57736 36.12424L309.02023 36.12424Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M317.74448 29.895252L316.61765 29.895252L316.61765 19.954746L317.8374 19.954746L317.8374 23.500774Q318.61572 22.53579 319.8123 22.53579Q320.47443 22.53579 321.0669 22.803194Q321.65936 23.0706 322.0427 23.558907Q322.42606 24.035585 322.63516 24.721539Q322.8559 25.407492 322.8559 26.186455Q322.8559 28.03504 321.93814 29.046532Q321.03204 30.058022 319.74258 30.058022Q318.47635 30.058022 317.74448 28.9884L317.74448 29.895252ZM317.73285 26.244587Q317.73285 27.535109 318.093 28.116425Q318.6622 29.058157 319.64963 29.058157Q320.4512 29.058157 321.03204 28.360579Q321.61288 27.662998 321.61288 26.291092Q321.61288 24.884306 321.05527 24.20998Q320.49768 23.535654 319.6961 23.535654Q318.90616 23.535654 318.3137 24.233232Q317.73285 24.930813 317.73285 26.244587Z" fill-rule="nonzero"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M340.913 5.0142717L381.14276 5.0142717L381.14276 44.480057L340.913 44.480057Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M340.913 5.0142717L381.14276 5.0142717L381.14276 44.480057L340.913 44.480057Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M356.37888 29.896229L360.1892 19.95572L361.60648 19.95572L365.67236 29.896229L364.1738 29.896229L363.0237 26.88501L358.8649 26.88501L357.77292 29.896229L356.37888 29.896229ZM359.24826 25.81539L362.61713 25.81539L361.57162 23.05995Q361.10693 21.804308 360.8746 21.00209Q360.67712 21.95545 360.34024 22.897182L359.24826 25.81539Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M421.37256 5.0123186L461.60233 5.0123186L461.60233 44.478104L421.37256 44.478104Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M421.37256 5.0123186L461.60233 5.0123186L461.60233 44.478104L421.37256 44.478104Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M437.87234 29.894276L437.87234 19.953768L441.60135 19.953768Q442.7398 19.953768 443.4252 20.256054Q444.1106 20.558338 444.49393 21.18616Q444.88892 21.81398 444.88892 22.499933Q444.88892 23.139381 444.5404 23.709072Q444.20352 24.267136 443.5065 24.604298Q444.401 24.871704 444.88892 25.511152Q445.37683 26.150599 445.37683 27.010946Q445.37683 27.708527 445.07477 28.313095Q444.78436 28.906038 444.34293 29.231575Q443.9131 29.557112 443.26254 29.731506Q442.612 29.894276 441.65942 29.894276L437.87234 29.894276ZM439.19666 24.127619L441.33417 24.127619Q442.21704 24.127619 442.58878 24.022982Q443.0883 23.87184 443.34387 23.52305Q443.59946 23.174261 443.59946 22.651075Q443.59946 22.16277 443.3555 21.790728Q443.12314 21.407059 442.6817 21.267544Q442.24026 21.128027 441.1715 21.128027L439.19666 21.128027L439.19666 24.127619ZM439.19666 28.720016L441.65942 28.720016Q442.29837 28.720016 442.55392 28.673512Q443.007 28.592127 443.30902 28.406107Q443.62268 28.208458 443.82016 27.848043Q444.01764 27.487627 444.01764 27.010946Q444.01764 26.452883 443.72723 26.045961Q443.44843 25.639042 442.9373 25.476273Q442.43774 25.301878 441.48517 25.301878L439.19666 25.301878L439.19666 28.720016Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M501.8321 5.0123186L542.0619 5.0123186L542.0619 44.478104L501.8321 44.478104Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M501.8321 5.0123186L542.0619 5.0123186L542.0619 44.478104L501.8321 44.478104Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M518.3319 29.894276L518.3319 19.953768L522.0609 19.953768Q523.19934 19.953768 523.88477 20.256054Q524.5701 20.558338 524.9535 21.18616Q525.34845 21.81398 525.34845 22.499933Q525.34845 23.139381 524.99994 23.709072Q524.6631 24.267136 523.96606 24.604298Q524.86053 24.871704 525.34845 25.511152Q525.83636 26.150599 525.83636 27.010946Q525.83636 27.708527 525.53436 28.313095Q525.2439 28.906038 524.8025 29.231575Q524.3726 29.557112 523.7221 29.731506Q523.07153 29.894276 522.11896 29.894276L518.3319 29.894276ZM519.6562 24.127619L521.7937 24.127619Q522.6766 24.127619 523.04834 24.022982Q523.54785 23.87184 523.8034 23.52305Q524.059 23.174261 524.059 22.651075Q524.059 22.16277 523.81506 21.790728Q523.5827 21.407059 523.14124 21.267544Q522.6998 21.128027 521.63104 21.128027L519.6562 21.128027L519.6562 24.127619ZM519.6562 28.720016L522.11896 28.720016Q522.75793 28.720016 523.0135 28.673512Q523.46655 28.592127 523.76855 28.406107Q524.0822 28.208458 524.2797 27.848043Q524.4772 27.487627 524.4772 27.010946Q524.4772 26.452883 524.18677 26.045961Q523.90796 25.639042 523.39685 25.476273Q522.89734 25.301878 521.9447 25.301878L519.6562 25.301878L519.6562 28.720016Z" fill-rule="nonzero"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M389.47977 13.369113L413.0369 13.369113L413.0369 36.125217L389.47977 36.125217Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7437813458751885,2.2313440376255658" clip-path="url(#id_0)" d="M389.47977 13.369113L413.0369 13.369113L413.0369 36.125217L389.47977 36.125217Z" fill-rule="evenodd"/><clipPath id="id_3"><path d="M5.684342E-14 0L583.0 0L583.0 130.0L5.684342E-14 130.0L5.684342E-14 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_3)" d="M401.782 29.012629Q401.09662 29.582317 400.4693 29.826471Q399.8536 30.058998 399.13336 30.058998Q397.94846 30.058998 397.30954 29.477682Q396.6706 28.896364 396.6706 28.001139Q396.6706 27.466328 396.91455 27.036154Q397.1585 26.594353 397.54187 26.338573Q397.93683 26.071169 398.42474 25.931652Q398.77325 25.838642 399.5051 25.745632Q400.98044 25.571236 401.67746 25.327084Q401.6891 25.071304 401.6891 25.013172Q401.6891 24.269089 401.34058 23.955177Q400.8759 23.548256 399.94656 23.548256Q399.0869 23.548256 398.68033 23.85054Q398.2737 24.152824 398.07623 24.908537L396.8797 24.745768Q397.04233 23.990057 397.41406 23.525003Q397.78583 23.048325 398.49445 22.792545Q399.20306 22.536766 400.1208 22.536766Q401.05014 22.536766 401.6194 22.757666Q402.20023 22.96694 402.4674 23.304104Q402.74622 23.62964 402.85077 24.129572Q402.90884 24.443483 402.90884 25.257326L402.90884 26.885012Q402.90884 28.582455 402.99017 29.035881Q403.07147 29.477682 403.2922 29.896229L402.02597 29.896229Q401.8285 29.51256 401.782 29.012629ZM401.67746 26.280441Q401.0153 26.559473 399.69098 26.745495Q398.93588 26.850132 398.62222 26.989649Q398.30856 27.129164 398.1343 27.384943Q397.97168 27.640722 397.97168 27.966259Q397.97168 28.454565 398.3434 28.780102Q398.71518 29.105639 399.4238 29.105639Q400.1208 29.105639 400.6668 28.803354Q401.2244 28.489443 401.47998 27.954634Q401.67746 27.536085 401.67746 26.73387L401.67746 26.280441Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M469.93933 13.369113L493.49646 13.369113L493.49646 36.125217L469.93933 36.125217Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7437813458751885,2.2313440376255658" clip-path="url(#id_0)" d="M469.93933 13.369113L493.49646 13.369113L493.49646 36.125217L469.93933 36.125217Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_3)" d="M478.66357 29.896229L477.53674 29.896229L477.53674 19.955723L478.7565 19.955723L478.7565 23.50175Q479.53485 22.536766 480.73138 22.536766Q481.39352 22.536766 481.986 22.80417Q482.57846 23.071577 482.9618 23.559883Q483.34515 24.036562 483.55426 24.722515Q483.775 25.408468 483.775 26.187431Q483.775 28.036016 482.85724 29.047508Q481.95114 30.058998 480.66168 30.058998Q479.39545 30.058998 478.66357 28.989376L478.66357 29.896229ZM478.65195 26.245564Q478.65195 27.536085 479.0121 28.117401Q479.5813 29.059134 480.56873 29.059134Q481.3703 29.059134 481.95114 28.361555Q482.53198 27.663975 482.53198 26.292068Q482.53198 24.885283 481.97437 24.210957Q481.41678 23.53663 480.6152 23.53663Q479.82526 23.53663 479.2328 24.23421Q478.65195 24.93179 478.65195 26.245564Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M550.39886 13.36716L573.956 13.36716L573.956 36.123264L550.39886 36.123264Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7437813458751885,2.2313440376255658" clip-path="url(#id_0)" d="M550.39886 13.36716L573.956 13.36716L573.956 36.123264L550.39886 36.123264Z" fill-rule="evenodd"/><clipPath id="id_4"><path d="M0 -1.7763568E-15L583.0 -1.7763568E-15L583.0 130.0L0 130.0L0 -1.7763568E-15Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_4)" d="M559.1231 29.894276L557.9963 29.894276L557.9963 19.95377L559.21606 19.95377L559.21606 23.499798Q559.9944 22.534813 561.1909 22.534813Q561.8531 22.534813 562.44556 22.802217Q563.038 23.069624 563.4213 23.55793Q563.8047 24.034609 564.0138 24.720562Q564.23456 25.406515 564.23456 26.185478Q564.23456 28.034063 563.31683 29.045555Q562.4107 30.057045 561.1212 30.057045Q559.855 30.057045 559.1231 28.987423L559.1231 29.894276ZM559.1115 26.24361Q559.1115 27.534132 559.4716 28.115448Q560.04083 29.05718 561.02826 29.05718Q561.82983 29.05718 562.4107 28.359602Q562.9915 27.662022 562.9915 26.290115Q562.9915 24.883331 562.4339 24.209003Q561.87634 23.534678 561.07477 23.534678Q560.2848 23.534678 559.6923 24.232256Q559.1115 24.929836 559.1115 26.24361Z" fill-rule="nonzero"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M92.20885 77.76369L271.3929 77.76369L271.3929 129.62698L92.20885 129.62698Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M92.20885 77.76369L271.3929 77.76369L271.3929 129.62698L92.20885 129.62698Z" fill-rule="evenodd"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M192.16273 83.95463L232.39252 83.95463L232.39252 123.42042L192.16273 123.42042Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M192.16273 83.95463L232.39252 83.95463L232.39252 123.42042L192.16273 123.42042Z" fill-rule="evenodd"/><clipPath id="id_5"><path d="M2.842171E-14 0L583.0 0L583.0 130.0L2.842171E-14 130.0L2.842171E-14 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_5)" d="M215.42424 105.348694L216.73694 105.68585Q216.31873 107.30191 215.24998 108.16226Q214.18123 109.01098 212.62457 109.01098Q211.02145 109.01098 210.01079 108.35991Q209.01173 107.697205 208.47736 106.46482Q207.9546 105.2208 207.9546 103.790764Q207.9546 102.24446 208.54706 101.09345Q209.13953 99.930824 210.2315 99.33788Q211.3351 98.733315 212.64781 98.733315Q214.13477 98.733315 215.14543 99.48902Q216.16771 100.244736 216.56268 101.628265L215.27321 101.93055Q214.92471 100.849304 214.26254 100.361Q213.612 99.86107 212.62457 99.86107Q211.4745 99.86107 210.7078 100.4075Q209.94109 100.95394 209.62743 101.88405Q209.31377 102.80253 209.31377 103.790764Q209.31377 105.05803 209.68552 105.99976Q210.05725 106.9415 210.83559 107.418175Q211.6139 107.88323 212.52002 107.88323Q213.62363 107.88323 214.39034 107.24378Q215.15704 106.60433 215.42424 105.348694Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M95.02957 83.95463L135.25935 83.95463L135.25935 123.42042L95.02957 123.42042Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M95.02957 83.95463L135.25935 83.95463L135.25935 123.42042L95.02957 123.42042Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M112.58026 108.836586L112.58026 98.89608L113.89296 98.89608L113.89296 108.836586L112.58026 108.836586ZM116.352615 108.836586L116.352615 102.58163L115.27225 102.58163L115.27225 101.63989L116.352615 101.63989L116.352615 100.87256Q116.352615 100.151726 116.4804 99.791306Q116.65465 99.31463 117.09609 99.02397Q117.53753 98.733315 118.339096 98.733315Q118.850235 98.733315 119.47755 98.84958L119.29167 99.9192Q118.91994 99.84944 118.583046 99.84944Q118.02544 99.84944 117.7931 100.09359Q117.56077 100.32612 117.56077 100.977196L117.56077 101.63989L118.9664 101.63989L118.9664 102.58163L117.56077 102.58163L117.56077 108.836586L116.352615 108.836586Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M143.59567 83.95463L183.82544 83.95463L183.82544 123.42042L143.59567 123.42042Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M143.59567 83.95463L183.82544 83.95463L183.82544 123.42042L143.59567 123.42042Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M156.52844 107.106125L156.67946 108.03623Q156.23802 108.12924 155.88951 108.12924Q155.3203 108.12924 155.00664 107.95485Q154.69298 107.76883 154.5652 107.47817Q154.43741 107.17589 154.43741 106.23415L154.43741 102.688126L153.6707 102.688126L153.6707 101.87428L154.43741 101.87428L154.43741 100.33961L155.48293 99.711784L155.48293 101.87428L156.52844 101.87428L156.52844 102.688126L155.48293 102.688126L155.48293 106.29228Q155.48293 106.74571 155.52939 106.873604Q155.58748 107.00149 155.71527 107.08288Q155.84305 107.15263 156.0754 107.15263Q156.24963 107.15263 156.52844 107.106125ZM157.54492 108.04786L157.54492 101.87428L158.48589 101.87428L158.48589 102.80439Q158.84601 102.15331 159.14804 101.94404Q159.45007 101.734764 159.82182 101.734764Q160.34457 101.734764 160.89056 102.07193L160.53044 103.03691Q160.1471 102.81602 159.76373 102.81602Q159.42685 102.81602 159.14804 103.02528Q158.88086 103.22294 158.76468 103.59498Q158.59044 104.15304 158.59044 104.81574L158.59044 108.04786L157.54492 108.04786ZM165.56055 108.04786L165.56055 107.14101Q164.8403 108.18738 163.59729 108.18738Q163.0513 108.18738 162.57501 107.9781Q162.11034 107.76883 161.878 107.45492Q161.64566 107.12938 161.55273 106.67596Q161.49464 106.362045 161.49464 105.69934L161.49464 101.87428L162.54016 101.87428L162.54016 105.29242Q162.54016 106.11789 162.59825 106.39692Q162.70279 106.81547 163.01645 107.048Q163.34172 107.280525 163.8064 107.280525Q164.28268 107.280525 164.68927 107.048Q165.10748 106.80384 165.27013 106.38529Q165.44437 105.96675 165.44437 105.176155L165.44437 101.87428L166.48988 101.87428L166.48988 108.04786L165.56055 108.04786ZM172.35059 106.05976L173.43095 106.187645Q173.17538 107.14101 172.47838 107.66419Q171.79298 108.18738 170.72423 108.18738Q169.36505 108.18738 168.57512 107.35028Q167.78517 106.51318 167.78517 105.01339Q167.78517 103.45546 168.58673 102.595116Q169.38829 101.734764 170.66614 101.734764Q171.89752 101.734764 172.67586 102.58349Q173.4658 103.420586 173.4658 104.943634Q173.4658 105.036644 173.4658 105.222664L168.86554 105.222664Q168.92361 106.24578 169.43475 106.79221Q169.95752 107.32703 170.72423 107.32703Q171.30507 107.32703 171.71165 107.02474Q172.11826 106.72246 172.35059 106.05976ZM168.92361 104.36231L172.3622 104.36231Q172.2925 103.58335 171.96722 103.199684Q171.46771 102.595116 170.67776 102.595116Q169.95752 102.595116 169.458 103.08342Q168.97008 103.5601 168.92361 104.36231Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M240.72952 92.308495L264.28665 92.308495L264.28665 115.0646L240.72952 115.0646Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7437813458751885,2.2313440376255658" clip-path="url(#id_0)" d="M240.72952 92.308495L264.28665 92.308495L264.28665 115.0646L240.72952 115.0646Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_5)" d="M253.03175 106.196434L254.22829 106.35921Q254.0308 107.5916 253.21762 108.300804Q252.41606 108.99838 251.23114 108.99838Q249.7558 108.99838 248.86131 108.033394Q247.96681 107.06841 247.96681 105.26633Q247.96681 104.09207 248.35017 103.2201Q248.73352 102.34812 249.52347 101.917946Q250.31342 101.47615 251.24277 101.47615Q252.41606 101.47615 253.15955 102.06909Q253.90302 102.66203 254.11212 103.754906L252.9272 103.929306Q252.75294 103.20847 252.32314 102.84805Q251.89331 102.47601 251.28923 102.47601Q250.37149 102.47601 249.79065 103.13871Q249.20981 103.801414 249.20981 105.23145Q249.20981 106.68474 249.76743 107.34744Q250.32503 107.99852 251.21953 107.99852Q251.93977 107.99852 252.41606 107.55672Q252.90398 107.114914 253.03175 106.196434Z" fill-rule="nonzero"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M302.36728 77.76369L477.24268 77.76369L477.24268 129.62698L302.36728 129.62698Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M302.36728 77.76369L477.24268 77.76369L477.24268 129.62698L302.36728 129.62698Z" fill-rule="evenodd"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M399.49945 83.95365L439.72925 83.95365L439.72925 123.41944L399.49945 123.41944Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M399.49945 83.95365L439.72925 83.95365L439.72925 123.41944L399.49945 123.41944Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M415.67468 108.83561L415.67468 98.8951L419.10165 98.8951Q420.2517 98.8951 420.8674 99.04625Q421.71542 99.23227 422.31952 99.755455Q423.10947 100.41815 423.49283 101.452896Q423.8878 102.48764 423.8878 103.81304Q423.8878 104.940796 423.6206 105.824394Q423.35342 106.696365 422.9352 107.26606Q422.52863 107.83575 422.0407 108.17291Q421.5528 108.49845 420.8558 108.672844Q420.15878 108.83561 419.26428 108.83561L415.67468 108.83561ZM416.9874 107.661354L419.11325 107.661354Q420.08908 107.661354 420.6467 107.48695Q421.20428 107.300934 421.5412 106.963776Q422.00586 106.49872 422.26144 105.70813Q422.52863 104.91754 422.52863 103.78979Q422.52863 102.23186 422.0175 101.39476Q421.50635 100.55767 420.77448 100.27863Q420.2517 100.06937 419.0784 100.06937L416.9874 100.06937L416.9874 107.661354Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M302.3663 83.95365L342.59607 83.95365L342.59607 123.41944L302.3663 123.41944Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M302.3663 83.95365L342.59607 83.95365L342.59607 123.41944L302.3663 123.41944Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M319.917 108.83561L319.917 98.8951L321.22968 98.8951L321.22968 108.83561L319.917 108.83561ZM323.68933 108.83561L323.68933 102.58065L322.60898 102.58065L322.60898 101.638916L323.68933 101.638916L323.68933 100.87158Q323.68933 100.15075 323.8171 99.79033Q323.99136 99.31365 324.4328 99.022995Q324.87424 98.73234 325.6758 98.73234Q326.18695 98.73234 326.81427 98.8486L326.6284 99.91822Q326.25665 99.848465 325.91977 99.848465Q325.36215 99.848465 325.12982 100.09261Q324.8975 100.32514 324.8975 100.97622L324.8975 101.638916L326.30313 101.638916L326.30313 102.58065L324.8975 102.58065L324.8975 108.83561L323.68933 108.83561Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M350.9324 83.95365L391.16217 83.95365L391.16217 123.41944L350.9324 123.41944Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M350.9324 83.95365L391.16217 83.95365L391.16217 123.41944L350.9324 123.41944Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M359.51755 108.04688L359.51755 102.68715L358.59982 102.68715L358.59982 101.87331L359.51755 101.87331L359.51755 101.2106Q359.51755 100.594406 359.63373 100.29212Q359.78476 99.8852 360.1565 99.629425Q360.53986 99.37364 361.22522 99.37364Q361.6667 99.37364 362.20105 99.47828L362.05002 100.39676Q361.72476 100.33863 361.43433 100.33863Q360.95804 100.33863 360.76056 100.547905Q360.56308 100.74555 360.56308 101.30361L360.56308 101.87331L361.7596 101.87331L361.7596 102.68715L360.56308 102.68715L360.56308 108.04688L359.51755 108.04688ZM366.59802 107.27955Q366.01718 107.77948 365.48282 107.988754Q364.94843 108.1864 364.33273 108.1864Q363.31046 108.1864 362.76447 107.68647Q362.21848 107.18654 362.21848 106.4192Q362.21848 105.965775 362.42758 105.59373Q362.6367 105.21006 362.96194 104.98916Q363.29883 104.75663 363.71704 104.64037Q364.0307 104.55898 364.6464 104.48923Q365.91263 104.33809 366.5167 104.128815Q366.5167 103.90791 366.5167 103.84978Q366.5167 103.210335 366.2263 102.95455Q365.8197 102.59414 365.02975 102.59414Q364.29788 102.59414 363.93777 102.86154Q363.58926 103.117325 363.42664 103.768394L362.40436 103.62888Q362.54373 102.977806 362.8574 102.570885Q363.18268 102.16396 363.78674 101.95469Q364.39084 101.73379 365.18076 101.73379Q365.9707 101.73379 366.45862 101.91981Q366.95816 102.105835 367.1905 102.384865Q367.42282 102.663895 367.51575 103.09407Q367.57385 103.36147 367.57385 104.05905L367.57385 105.454216Q367.57385 106.91913 367.63193 107.302795Q367.70163 107.68647 367.8991 108.04688L366.80713 108.04688Q366.6445 107.721344 366.59802 107.27955ZM366.5167 104.94266Q365.94748 105.17518 364.80902 105.33795Q364.15848 105.43096 363.8913 105.547226Q363.6241 105.66349 363.47308 105.89601Q363.33368 106.11691 363.33368 106.384315Q363.33368 106.802864 363.64734 107.0819Q363.97263 107.36093 364.5767 107.36093Q365.18076 107.36093 365.64545 107.10515Q366.12173 106.837746 366.34247 106.372696Q366.5167 106.0239 366.5167 105.326324L366.5167 104.94266ZM369.17114 108.04688L369.17114 99.52479L370.21667 99.52479L370.21667 108.04688L369.17114 108.04688ZM371.419 106.198296L372.4529 106.03553Q372.53424 106.66335 372.9292 107.00051Q373.3358 107.32605 374.04443 107.32605Q374.76468 107.32605 375.11316 107.03539Q375.46167 106.73311 375.46167 106.337814Q375.46167 105.98902 375.15964 105.77975Q374.93893 105.640236 374.09088 105.43096Q372.94083 105.140305 372.48776 104.93103Q372.04633 104.71013 371.814 104.33809Q371.59326 103.96604 371.59326 103.51262Q371.59326 103.09407 371.77914 102.74528Q371.965 102.39649 372.29028 102.16396Q372.53424 101.97794 372.95245 101.86168Q373.38226 101.73379 373.85855 101.73379Q374.59042 101.73379 375.1364 101.94306Q375.6824 102.152336 375.93796 102.51276Q376.20517 102.86154 376.30972 103.46611L375.2874 103.60563Q375.2177 103.128944 374.88083 102.86154Q374.54395 102.59414 373.93988 102.59414Q373.21964 102.59414 372.90598 102.83829Q372.60394 103.070816 372.60394 103.38473Q372.60394 103.594 372.73172 103.75677Q372.8595 103.91954 373.12668 104.035805Q373.28934 104.09393 374.05603 104.30321Q375.15964 104.593864 375.60107 104.79151Q376.0425 104.97753 376.28647 105.33795Q376.54205 105.698364 376.54205 106.23318Q376.54205 106.75636 376.2284 107.22141Q375.92636 107.67484 375.34552 107.93062Q374.77628 108.1864 374.05603 108.1864Q372.8479 108.1864 372.22058 107.68647Q371.59326 107.18654 371.419 106.198296ZM382.00195 106.058784L383.08234 106.18667Q382.82675 107.14003 382.12976 107.663216Q381.44437 108.1864 380.3756 108.1864Q379.01642 108.1864 378.2265 107.349304Q377.43655 106.51221 377.43655 105.01241Q377.43655 103.45448 378.2381 102.59414Q379.03967 101.73379 380.3175 101.73379Q381.54892 101.73379 382.32724 102.58251Q383.1172 103.41961 383.1172 104.94266Q383.1172 105.03567 383.1172 105.22169L378.5169 105.22169Q378.57498 106.244804 379.08612 106.79124Q379.6089 107.32605 380.3756 107.32605Q380.95645 107.32605 381.36304 107.023766Q381.76962 106.72148 382.00195 106.058784ZM378.57498 104.361336L382.01358 104.361336Q381.94388 103.582375 381.6186 103.19871Q381.11908 102.59414 380.32913 102.59414Q379.6089 102.59414 379.10938 103.08244Q378.62146 103.55912 378.57498 104.361336Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M448.06622 92.31631L471.62338 92.31631L471.62338 115.07241L448.06622 115.07241Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7437813458751885" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7437813458751885,2.2313440376255658" clip-path="url(#id_0)" d="M448.06622 92.31631L471.62338 92.31631L471.62338 115.07241L448.06622 115.07241Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M460.33362 108.84342L460.33362 107.93657Q459.65985 109.006195 458.3239 109.006195Q457.46426 109.006195 456.74402 108.52951Q456.02377 108.05283 455.6288 107.20411Q455.23383 106.35539 455.23383 105.25089Q455.23383 104.16964 455.59396 103.29767Q455.95407 102.41406 456.6627 101.94901Q457.38293 101.48396 458.27744 101.48396Q458.92798 101.48396 459.42752 101.76299Q459.93866 102.030396 460.25232 102.4722L460.25232 98.902916L461.47208 98.902916L461.47208 108.84342L460.33362 108.84342ZM456.48843 105.25089Q456.48843 106.63442 457.0693 107.32037Q457.65015 108.006325 458.44006 108.006325Q459.24164 108.006325 459.79926 107.355255Q460.35687 106.69255 460.35687 105.35553Q460.35687 103.87898 459.78763 103.19303Q459.2184 102.49545 458.382 102.49545Q457.58044 102.49545 457.03445 103.15815Q456.48843 103.820854 456.48843 105.25089Z" fill-rule="nonzero"/></svg>
\ No newline at end of file
diff --git a/compose/runtime/design/images/call-tree-4.svg b/compose/runtime/design/images/call-tree-4.svg
new file mode 100644
index 0000000..31afc98
--- /dev/null
+++ b/compose/runtime/design/images/call-tree-4.svg
@@ -0,0 +1 @@
+<svg version="1.1" viewBox="0.0 0.0 583.0 33.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" preserveAspectRatio="none" width="583" height="33" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="id_0"><path d="M0 0L583.0 0L583.0 33.0L0 33.0L0 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M-5.7342677 -3.64448L583.0 -3.64448L583.0 444.4244L-5.7342677 444.4244Z" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M207.83824 0.23398173L318.5591 0.23398173L318.5591 32.766018L207.83824 32.766018Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M207.83824 0.23398173L318.5591 0.23398173L318.5591 32.766018L207.83824 32.766018Z" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M0.23057793 4.122244L52.845806 4.122244L52.845806 28.877756L0.23057793 28.877756Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M0.23057793 4.122244L52.845806 4.122244L52.845806 28.877756L0.23057793 28.877756Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M18.150986 19.729828L18.150986 13.494494L20.874744 13.494494Q21.694027 13.494494 22.118042 13.662229Q22.549244 13.829963 22.80078 14.260238Q23.0595 14.683219 23.0595 15.201007Q23.0595 15.857358 22.635485 16.316803Q22.21147 16.768957 21.327505 16.892935Q21.650908 17.053375 21.82339 17.206524Q22.175537 17.534698 22.49175 18.03061L23.562569 19.729828L22.542057 19.729828L21.729961 18.431713Q21.370626 17.870167 21.140652 17.578457Q20.910677 17.279451 20.723824 17.162767Q20.544155 17.03879 20.357302 16.995033Q20.220755 16.958569 19.90454 16.958569L18.963083 16.958569L18.963083 19.729828L18.150986 19.729828ZM18.963083 16.25117L20.70945 16.25117Q21.270012 16.25117 21.57904 16.134483Q21.895256 16.010508 22.060549 15.7552595Q22.225843 15.500011 22.225843 15.201007Q22.225843 14.756147 21.909628 14.471728Q21.593414 14.187309 20.90349 14.187309L18.963083 14.187309L18.963083 16.25117ZM23.959766 17.469065Q23.959766 16.214705 24.642504 15.616696Q25.21744 15.113494 26.04391 15.113494Q26.963806 15.113494 27.545929 15.726088Q28.128052 16.331388 28.128052 17.410723Q28.128052 18.278564 27.869331 18.781767Q27.61061 19.277676 27.114727 19.554802Q26.626032 19.831928 26.04391 19.831928Q25.109638 19.831928 24.534702 19.226625Q23.959766 18.614033 23.959766 17.469065ZM24.735931 17.469065Q24.735931 18.336906 25.102451 18.774473Q25.47616 19.204748 26.04391 19.204748Q26.604471 19.204748 26.97818 18.774473Q27.351889 18.336906 27.351889 17.447186Q27.351889 16.608515 26.97818 16.17824Q26.604471 15.747966 26.04391 15.747966Q25.47616 15.747966 25.102451 16.17824Q24.735931 16.601223 24.735931 17.469065ZM28.732065 17.469065Q28.732065 16.214705 29.4148 15.616696Q29.989738 15.113494 30.816208 15.113494Q31.736105 15.113494 32.31823 15.726088Q32.900352 16.331388 32.900352 17.410723Q32.900352 18.278564 32.64163 18.781767Q32.382908 19.277676 31.887026 19.554802Q31.39833 19.831928 30.816208 19.831928Q29.881937 19.831928 29.307001 19.226625Q28.732065 18.614033 28.732065 17.469065ZM29.508228 17.469065Q29.508228 18.336906 29.87475 18.774473Q30.248459 19.204748 30.816208 19.204748Q31.37677 19.204748 31.750479 18.774473Q32.124187 18.336906 32.124187 17.447186Q32.124187 16.608515 31.750479 16.17824Q31.37677 15.747966 30.816208 15.747966Q30.248459 15.747966 29.87475 16.17824Q29.508228 16.601223 29.508228 17.469065ZM35.430397 19.044306L35.5382 19.722536Q35.221985 19.788172 34.97045 19.788172Q34.560806 19.788172 34.330833 19.6569Q34.108047 19.52563 34.014618 19.31414Q33.921192 19.095356 33.921192 18.409834L33.921192 15.806309L33.367817 15.806309L33.367817 15.215592L33.921192 15.215592L33.921192 14.099796L34.66861 13.64035L34.66861 15.215592L35.430397 15.215592L35.430397 15.806309L34.66861 15.806309L34.66861 18.45359Q34.66861 18.774473 34.70454 18.86928Q34.74766 18.964087 34.8339 19.022429Q34.92733 19.073477 35.09981 19.073477Q35.221985 19.073477 35.430397 19.044306Z" fill-rule="nonzero"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M58.003025 4.122244L82.89095 4.122244L82.89095 28.877756L58.003025 28.877756Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M58.003025 4.122244L82.89095 4.122244L82.89095 28.877756L58.003025 28.877756Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M67.570915 19.729828L69.928154 13.494494L70.80493 13.494494L73.320274 19.729828L72.39319 19.729828L71.68171 17.840996L69.10887 17.840996L68.43332 19.729828L67.570915 19.729828ZM69.34603 17.17006L71.43017 17.17006L70.78337 15.441669Q70.4959 14.654048 70.352165 14.150846Q70.229996 14.748855 70.021576 15.33957L69.34603 17.17006Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M107.778885 4.1173434L132.66681 4.1173434L132.66681 28.872856L107.778885 28.872856Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M107.778885 4.1173434L132.66681 4.1173434L132.66681 28.872856L107.778885 28.872856Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M117.98639 19.724928L117.98639 13.489594L120.29332 13.489594Q120.99762 13.489594 121.42163 13.679207Q121.84565 13.868819 122.08281 14.26263Q122.327156 14.656441 122.327156 15.086715Q122.327156 15.487819 122.11156 15.845165Q121.90314 16.195219 121.47194 16.40671Q122.025314 16.574444 122.327156 16.975548Q122.629 17.37665 122.629 17.916317Q122.629 18.353884 122.44215 18.733109Q122.262474 19.105042 121.98938 19.30924Q121.72347 19.513437 121.32102 19.62283Q120.918564 19.724928 120.329254 19.724928L117.98639 19.724928ZM118.80567 16.107706L120.12803 16.107706Q120.67422 16.107706 120.90419 16.04207Q121.21322 15.947265 121.37132 15.72848Q121.529434 15.509697 121.529434 15.181521Q121.529434 14.875224 121.37851 14.641855Q121.23478 14.401193 120.961685 14.31368Q120.68859 14.226166 120.02741 14.226166L118.80567 14.226166L118.80567 16.107706ZM118.80567 18.988358L120.329254 18.988358Q120.724525 18.988358 120.88263 18.959187Q121.16291 18.908136 121.34976 18.791452Q121.54381 18.667475 121.66598 18.441399Q121.788155 18.21532 121.788155 17.916317Q121.788155 17.566263 121.60849 17.311016Q121.436005 17.055767 121.11979 16.953669Q120.81076 16.844276 120.22145 16.844276L118.80567 16.844276L118.80567 18.988358Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M157.55475 4.1124434L182.44267 4.1124434L182.44267 28.867956L157.55475 28.867956Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M157.55475 4.1124434L182.44267 4.1124434L182.44267 28.867956L157.55475 28.867956Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M167.76225 19.720028L167.76225 13.4846945L170.06918 13.4846945Q170.77348 13.4846945 171.1975 13.674307Q171.6215 13.863919 171.85867 14.2577305Q172.10301 14.651541 172.10301 15.081815Q172.10301 15.482919 171.88742 15.840265Q171.679 16.19032 171.2478 16.40181Q171.80118 16.569544 172.10301 16.970648Q172.40486 17.37175 172.40486 17.911417Q172.40486 18.348984 172.218 18.728209Q172.03833 19.100142 171.76524 19.304338Q171.49933 19.508537 171.09688 19.61793Q170.69443 19.720028 170.10512 19.720028L167.76225 19.720028ZM168.58153 16.102806L169.90388 16.102806Q170.45007 16.102806 170.68005 16.03717Q170.98907 15.942364 171.14719 15.72358Q171.3053 15.504797 171.3053 15.176621Q171.3053 14.870324 171.15437 14.636955Q171.01064 14.396293 170.73755 14.30878Q170.46445 14.221266 169.80327 14.221266L168.58153 14.221266L168.58153 16.102806ZM168.58153 18.983458L170.10512 18.983458Q170.50038 18.983458 170.6585 18.954285Q170.93877 18.903236 171.12563 18.78655Q171.31967 18.662575 171.44183 18.436497Q171.56401 18.21042 171.56401 17.911417Q171.56401 17.561363 171.38435 17.306116Q171.21187 17.050867 170.89565 16.948769Q170.58662 16.839376 169.99731 16.839376L168.58153 16.839376L168.58153 18.983458Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M272.31134 4.1173434L297.19925 4.1173434L297.19925 28.872856L272.31134 28.872856Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M272.31134 4.1173434L297.19925 4.1173434L297.19925 28.872856L272.31134 28.872856Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M286.70193 17.537092L287.514 17.748583Q287.2553 18.76228 286.59412 19.301947Q285.93295 19.83432 284.96994 19.83432Q283.97818 19.83432 283.35294 19.425924Q282.73486 19.010235 282.40427 18.2372Q282.08087 17.456871 282.08087 16.559858Q282.08087 15.589917 282.4474 14.867931Q282.81393 14.138653 283.48947 13.766721Q284.1722 13.387496 284.9843 13.387496Q285.9042 13.387496 286.52945 13.8615265Q287.16187 14.335558 287.40622 15.2034L286.6085 15.393012Q286.39288 14.714783 285.98325 14.408486Q285.5808 14.094896 284.96994 14.094896Q284.25845 14.094896 283.78412 14.437657Q283.3098 14.780418 283.11575 15.363841Q282.92172 15.939971 282.92172 16.559858Q282.92172 17.354773 283.1517 17.945488Q283.38168 18.536203 283.8632 18.835209Q284.3447 19.12692 284.90524 19.12692Q285.58798 19.12692 286.06232 18.725817Q286.53662 18.324713 286.70193 17.537092Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M212.22043 4.1173434L237.10835 4.1173434L237.10835 28.872856L212.22043 28.872856Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M212.22043 4.1173434L237.10835 4.1173434L237.10835 28.872856L212.22043 28.872856Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M223.07806 19.724928L223.07806 13.489594L223.89015 13.489594L223.89015 19.724928L223.07806 19.724928ZM225.4118 19.724928L225.4118 15.801409L224.74344 15.801409L224.74344 15.210692L225.4118 15.210692L225.4118 14.729368Q225.4118 14.277216 225.49086 14.051139Q225.59866 13.752135 225.87175 13.569816Q226.14485 13.387496 226.64073 13.387496Q226.95695 13.387496 227.34503 13.460423L227.23004 14.13136Q227.00006 14.087604 226.79166 14.087604Q226.44669 14.087604 226.30296 14.240752Q226.15923 14.386607 226.15923 14.795004L226.15923 15.210692L227.02881 15.210692L227.02881 15.801409L226.15923 15.801409L226.15923 19.724928L225.4118 19.724928Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M242.26558 4.1173434L267.1535 4.1173434L267.1535 28.872856L242.26558 28.872856Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M242.26558 4.1173434L267.1535 4.1173434L267.1535 28.872856L242.26558 28.872856Z" fill-rule="evenodd"/><clipPath id="id_1"><path d="M-2.842171E-14 0L583.0 0L583.0 33.0L-2.842171E-14 33.0L-2.842171E-14 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_1)" d="M250.26637 18.639471L250.35979 19.222895Q250.0867 19.281237 249.8711 19.281237Q249.51895 19.281237 249.3249 19.171844Q249.13086 19.05516 249.05182 18.87284Q248.97276 18.683228 248.97276 18.092512L248.97276 15.868212L248.49844 15.868212L248.49844 15.357717L248.97276 15.357717L248.97276 14.395068L249.61957 14.001258L249.61957 15.357717L250.26637 15.357717L250.26637 15.868212L249.61957 15.868212L249.61957 18.128975Q249.61957 18.413395 249.64832 18.493616Q249.68425 18.573835 249.76329 18.624886Q249.84235 18.668642 249.98608 18.668642Q250.09389 18.668642 250.26637 18.639471ZM250.8952 19.230186L250.8952 15.357717L251.47733 15.357717L251.47733 15.941139Q251.7001 15.532743 251.88696 15.401473Q252.07382 15.270203 252.30379 15.270203Q252.6272 15.270203 252.96497 15.481694L252.74219 16.086996Q252.50502 15.948432 252.26787 15.948432Q252.05945 15.948432 251.88696 16.079702Q251.72166 16.20368 251.64981 16.43705Q251.542 16.787104 251.542 17.202791L251.542 19.230186L250.8952 19.230186ZM255.85402 19.230186L255.85402 18.66135Q255.40845 19.3177 254.63947 19.3177Q254.3017 19.3177 254.00703 19.18643Q253.71957 19.05516 253.57584 18.858255Q253.4321 18.654057 253.37462 18.369638Q253.33868 18.172733 253.33868 17.757044L253.33868 15.357717L253.98547 15.357717L253.98547 17.501797Q253.98547 18.019585 254.02141 18.19461Q254.08609 18.457151 254.28014 18.603006Q254.48137 18.748863 254.76883 18.748863Q255.06349 18.748863 255.31502 18.603006Q255.57375 18.449858 255.67436 18.187319Q255.78215 17.924778 255.78215 17.428867L255.78215 15.357717L256.42896 15.357717L256.42896 19.230186L255.85402 19.230186ZM260.05466 17.98312L260.72302 18.063341Q260.5649 18.66135 260.1337 18.989525Q259.7097 19.3177 259.04852 19.3177Q258.20767 19.3177 257.71896 18.79262Q257.2303 18.267538 257.2303 17.326769Q257.2303 16.349535 257.72617 15.809869Q258.22205 15.270203 259.01257 15.270203Q259.77435 15.270203 260.2559 15.802576Q260.74457 16.327658 260.74457 17.283012Q260.74457 17.341354 260.74457 17.45804L257.89865 17.45804Q257.93457 18.099804 258.2508 18.442566Q258.5742 18.778034 259.04852 18.778034Q259.40784 18.778034 259.6594 18.58842Q259.91092 18.39881 260.05466 17.98312ZM257.93457 16.918373L260.06183 16.918373Q260.0187 16.429756 259.81747 16.189095Q259.50845 15.809869 259.01978 15.809869Q258.5742 15.809869 258.26517 16.116167Q257.96332 16.41517 257.93457 16.918373Z" fill-rule="nonzero"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M88.04859 9.362945L102.622086 9.362945L102.622086 23.637056L88.04859 23.637056Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.46333110432708835,1.389993312981265" clip-path="url(#id_0)" d="M88.04859 9.362945L102.622086 9.362945L102.622086 23.637056L88.04859 23.637056Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M95.65931 19.175577Q95.23529 19.532923 94.84721 19.686071Q94.46632 19.831928 94.02074 19.831928Q93.2877 19.831928 92.892426 19.467289Q92.49716 19.10265 92.49716 18.541105Q92.49716 18.205637 92.64808 17.935802Q92.799 17.658676 93.03616 17.498236Q93.28051 17.330502 93.58235 17.242989Q93.79795 17.184647 94.25072 17.126303Q95.16342 17.016912 95.59463 16.863764Q95.601814 16.703321 95.601814 16.666859Q95.601814 16.200119 95.386215 16.003214Q95.09875 15.747967 94.52381 15.747967Q93.992 15.747967 93.740456 15.937579Q93.48892 16.127192 93.36675 16.601223L92.62652 16.499125Q92.727135 16.025093 92.95711 15.733381Q93.18708 15.434377 93.62547 15.273935Q94.06386 15.113494 94.63161 15.113494Q95.20654 15.113494 95.55869 15.252057Q95.91803 15.3833275 96.08332 15.594818Q96.255806 15.799016 96.32048 16.112606Q96.356415 16.309511 96.356415 16.820007L96.356415 17.840996Q96.356415 18.905745 96.40672 19.190163Q96.45703 19.467289 96.593575 19.72983L95.81023 19.72983Q95.68806 19.489166 95.65931 19.175577ZM95.59463 17.461771Q95.18498 17.636799 94.3657 17.753483Q93.89857 17.819118 93.70453 17.906631Q93.51048 17.994144 93.40269 18.154587Q93.30207 18.315027 93.30207 18.519226Q93.30207 18.825523 93.53204 19.02972Q93.76202 19.23392 94.20041 19.23392Q94.63161 19.23392 94.96938 19.044308Q95.31435 18.8474 95.47245 18.511932Q95.59463 18.249393 95.59463 17.746191L95.59463 17.461771Z" fill-rule="nonzero"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M187.59953 9.353145L202.17302 9.353145L202.17302 23.627254L187.59953 23.627254Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.46333110432708835,1.389993312981265" clip-path="url(#id_0)" d="M187.59953 9.353145L202.17302 9.353145L202.17302 23.627254L187.59953 23.627254Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M192.99673 19.720028L192.29962 19.720028L192.29962 13.4846945L193.05423 13.4846945L193.05423 15.708995Q193.53574 15.103694 194.27597 15.103694Q194.68561 15.103694 195.05214 15.271428Q195.41866 15.439162 195.6558 15.745459Q195.89297 16.044464 196.02234 16.474737Q196.15889 16.905012 196.15889 17.39363Q196.15889 18.553183 195.59114 19.187654Q195.03058 19.822128 194.23285 19.822128Q193.4495 19.822128 192.99673 19.151192L192.99673 19.720028ZM192.98955 17.430094Q192.98955 18.239594 193.21234 18.604233Q193.56448 19.194948 194.17535 19.194948Q194.67123 19.194948 195.03058 18.757381Q195.38991 18.319813 195.38991 17.459265Q195.38991 16.576838 195.04495 16.153854Q194.69998 15.730873 194.2041 15.730873Q193.71541 15.730873 193.34889 16.16844Q192.98955 16.606009 192.98955 17.430094Z" fill-rule="nonzero"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M137.82462 9.353145L152.3981 9.353145L152.3981 23.627254L137.82462 23.627254Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.46333110432708835,1.389993312981265" clip-path="url(#id_0)" d="M137.82462 9.353145L152.3981 9.353145L152.3981 23.627254L137.82462 23.627254Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M143.22182 19.720028L142.52472 19.720028L142.52472 13.4846945L143.27931 13.4846945L143.27931 15.708995Q143.76082 15.103694 144.50105 15.103694Q144.91069 15.103694 145.27722 15.271428Q145.64374 15.439162 145.8809 15.745459Q146.11806 16.044464 146.24742 16.474737Q146.38397 16.905012 146.38397 17.39363Q146.38397 18.553183 145.81622 19.187654Q145.25566 19.822128 144.45793 19.822128Q143.67459 19.822128 143.22182 19.151192L143.22182 19.720028ZM143.21463 17.430094Q143.21463 18.239594 143.43742 18.604233Q143.78957 19.194948 144.40044 19.194948Q144.89632 19.194948 145.25566 18.757381Q145.61499 18.319813 145.61499 17.459265Q145.61499 16.576838 145.27003 16.153854Q144.92506 15.730873 144.42918 15.730873Q143.94049 15.730873 143.57397 16.16844Q143.21463 16.606009 143.21463 17.430094Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M301.4874 9.353145L316.06088 9.353145L316.06088 23.627254L301.4874 23.627254Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.46333110432708835,1.389993312981265" clip-path="url(#id_0)" d="M301.4874 9.353145L316.06088 9.353145L316.06088 23.627254L301.4874 23.627254Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M309.0981 18.064566L309.83832 18.166664Q309.71616 18.939701 309.21307 19.38456Q308.7172 19.822128 307.98416 19.822128Q307.07144 19.822128 306.51807 19.216825Q305.9647 18.611525 305.9647 17.481142Q305.9647 16.744572 306.20187 16.197613Q306.43903 15.650653 306.9277 15.380819Q307.4164 15.103694 307.99136 15.103694Q308.7172 15.103694 309.17715 15.475626Q309.63712 15.847558 309.76648 16.533081L309.03342 16.642471Q308.92563 16.19032 308.6597 15.964243Q308.3938 15.730873 308.0201 15.730873Q307.45236 15.730873 307.09302 16.146563Q306.73367 16.562252 306.73367 17.459265Q306.73367 18.370863 307.07864 18.786552Q307.4236 19.194948 307.977 19.194948Q308.42255 19.194948 308.7172 18.917822Q309.01904 18.640696 309.0981 18.064566Z" fill-rule="nonzero"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M474.05795 0.23398173L584.7788 0.23398173L584.7788 32.766018L474.05795 32.766018Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M474.05795 0.23398173L584.7788 0.23398173L584.7788 32.766018L474.05795 32.766018Z" fill-rule="evenodd"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M324.22275 4.122244L349.1107 4.122244L349.1107 28.877756L324.22275 28.877756Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M324.22275 4.122244L349.1107 4.122244L349.1107 28.877756L324.22275 28.877756Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M333.79065 19.729828L336.1479 13.494494L337.02466 13.494494L339.54 19.729828L338.6129 19.729828L337.90143 17.840996L335.32858 17.840996L334.65305 19.729828L333.79065 19.729828ZM335.56577 17.17006L337.6499 17.17006L337.00308 15.441669Q336.71564 14.654048 336.5719 14.150846Q336.4497 14.748855 336.2413 15.33957L335.56577 17.17006Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M373.9986 4.1173434L398.88654 4.1173434L398.88654 28.872856L373.9986 28.872856Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M373.9986 4.1173434L398.88654 4.1173434L398.88654 28.872856L373.9986 28.872856Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M384.20612 19.724928L384.20612 13.489594L386.51303 13.489594Q387.21735 13.489594 387.64136 13.679207Q388.06537 13.868819 388.30252 14.26263Q388.54688 14.656441 388.54688 15.086715Q388.54688 15.487819 388.33127 15.845165Q388.12286 16.195219 387.69165 16.40671Q388.24503 16.574444 388.54688 16.975548Q388.84872 17.37665 388.84872 17.916317Q388.84872 18.353884 388.66187 18.733109Q388.4822 19.105042 388.2091 19.30924Q387.9432 19.513437 387.54074 19.62283Q387.13828 19.724928 386.54898 19.724928L384.20612 19.724928ZM385.0254 16.107706L386.34775 16.107706Q386.89395 16.107706 387.1239 16.04207Q387.43295 15.947265 387.59106 15.72848Q387.74915 15.509697 387.74915 15.181521Q387.74915 14.875224 387.59824 14.641855Q387.4545 14.401193 387.1814 14.31368Q386.90833 14.226166 386.24713 14.226166L385.0254 14.226166L385.0254 16.107706ZM385.0254 18.988358L386.54898 18.988358Q386.94424 18.988358 387.10236 18.959187Q387.38263 18.908136 387.5695 18.791452Q387.76352 18.667475 387.8857 18.441399Q388.00787 18.21532 388.00787 17.916317Q388.00787 17.566263 387.82822 17.311016Q387.65573 17.055767 387.3395 16.953669Q387.0305 16.844276 386.4412 16.844276L385.0254 16.844276L385.0254 18.988358Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M423.77448 4.1124434L448.6624 4.1124434L448.6624 28.867956L423.77448 28.867956Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M423.77448 4.1124434L448.6624 4.1124434L448.6624 28.867956L423.77448 28.867956Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M433.98196 19.720028L433.98196 13.4846945L436.2889 13.4846945Q436.9932 13.4846945 437.4172 13.674307Q437.84125 13.863919 438.0784 14.2577305Q438.32275 14.651541 438.32275 15.081815Q438.32275 15.482919 438.10715 15.840265Q437.8987 16.19032 437.46753 16.40181Q438.0209 16.569544 438.32275 16.970648Q438.62457 17.37175 438.62457 17.911417Q438.62457 18.348984 438.4377 18.728209Q438.25806 19.100142 437.98495 19.304338Q437.71906 19.508537 437.3166 19.61793Q436.91415 19.720028 436.32483 19.720028L433.98196 19.720028ZM434.80127 16.102806L436.12363 16.102806Q436.6698 16.102806 436.89978 16.03717Q437.2088 15.942364 437.3669 15.72358Q437.52502 15.504797 437.52502 15.176621Q437.52502 14.870324 437.37408 14.636955Q437.23038 14.396293 436.95728 14.30878Q436.68417 14.221266 436.023 14.221266L434.80127 14.221266L434.80127 16.102806ZM434.80127 18.983458L436.32483 18.983458Q436.7201 18.983458 436.8782 18.954285Q437.1585 18.903236 437.34534 18.78655Q437.5394 18.662575 437.66156 18.436497Q437.78375 18.21042 437.78375 17.911417Q437.78375 17.561363 437.60406 17.306116Q437.43158 17.050867 437.1154 16.948769Q436.80634 16.839376 436.21704 16.839376L434.80127 16.839376L434.80127 18.983458Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M538.53107 4.1173434L563.419 4.1173434L563.419 28.872856L538.53107 28.872856Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M538.53107 4.1173434L563.419 4.1173434L563.419 28.872856L538.53107 28.872856Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M548.5378 19.724928L548.5378 13.489594L550.65784 13.489594Q551.3693 13.489594 551.75024 13.584401Q552.27484 13.701085 552.64856 14.029261Q553.13727 14.44495 553.3744 15.094008Q553.6188 15.743066 553.6188 16.574444Q553.6188 17.281845 553.4535 17.836096Q553.28815 18.383055 553.0295 18.740402Q552.7779 19.097748 552.4761 19.30924Q552.17426 19.513437 551.74304 19.62283Q551.3118 19.724928 550.7585 19.724928L548.5378 19.724928ZM549.34985 18.988358L550.66504 18.988358Q551.26874 18.988358 551.61365 18.878965Q551.9586 18.76228 552.16705 18.550789Q552.4545 18.259077 552.6126 17.763168Q552.7779 17.26726 552.7779 16.559858Q552.7779 15.582624 552.46173 15.057544Q552.1455 14.532463 551.69275 14.357436Q551.3693 14.226166 550.6435 14.226166L549.34985 14.226166L549.34985 18.988358Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M478.44016 4.1173434L503.3281 4.1173434L503.3281 28.872856L478.44016 28.872856Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M478.44016 4.1173434L503.3281 4.1173434L503.3281 28.872856L478.44016 28.872856Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M489.2978 19.724928L489.2978 13.489594L490.1099 13.489594L490.1099 19.724928L489.2978 19.724928ZM491.63153 19.724928L491.63153 15.801409L490.96317 15.801409L490.96317 15.210692L491.63153 15.210692L491.63153 14.729368Q491.63153 14.277216 491.7106 14.051139Q491.8184 13.752135 492.0915 13.569816Q492.36456 13.387496 492.86047 13.387496Q493.17667 13.387496 493.56476 13.460423L493.44977 14.13136Q493.2198 14.087604 493.01138 14.087604Q492.6664 14.087604 492.52267 14.240752Q492.37894 14.386607 492.37894 14.795004L492.37894 15.210692L493.24854 15.210692L493.24854 15.801409L492.37894 15.801409L492.37894 19.724928L491.63153 19.724928Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M508.4853 4.1173434L533.3732 4.1173434L533.3732 28.872856L508.4853 28.872856Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M508.4853 4.1173434L533.3732 4.1173434L533.3732 28.872856L508.4853 28.872856Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M513.79645 19.230186L513.79645 15.868212L513.2287 15.868212L513.2287 15.357717L513.79645 15.357717L513.79645 14.942027Q513.79645 14.55551 513.86835 14.365897Q513.9618 14.11065 514.1917 13.950209Q514.4289 13.789767 514.8529 13.789767Q515.12604 13.789767 515.4566 13.855402L515.36316 14.431532Q515.1619 14.395068 514.9823 14.395068Q514.6876 14.395068 514.5654 14.526339Q514.4433 14.650316 514.4433 15.00037L514.4433 15.357717L515.1835 15.357717L515.1835 15.868212L514.4433 15.868212L514.4433 19.230186L513.79645 19.230186ZM518.17676 18.748863Q517.81744 19.062452 517.4868 19.193724Q517.15625 19.3177 516.7753 19.3177Q516.14294 19.3177 515.8052 19.00411Q515.46735 18.690521 515.46735 18.209196Q515.46735 17.924778 515.59674 17.691408Q515.7261 17.450747 515.9273 17.312183Q516.13574 17.166328 516.3945 17.0934Q516.5885 17.04235 516.9694 16.998594Q517.75275 16.903788 518.12646 16.772518Q518.12646 16.633955 518.12646 16.59749Q518.12646 16.196386 517.9468 16.035946Q517.69525 15.809869 517.20654 15.809869Q516.7538 15.809869 516.531 15.977603Q516.3154 16.138044 516.2148 16.54644L515.58234 16.458927Q515.6686 16.050531 515.8627 15.795283Q516.06384 15.540036 516.43756 15.408766Q516.8113 15.270203 517.3 15.270203Q517.7887 15.270203 518.0905 15.386888Q518.39954 15.503572 518.5433 15.678599Q518.687 15.853626 518.7445 16.123459Q518.78046 16.291193 518.78046 16.72876L518.78046 17.603895Q518.78046 18.522787 518.8164 18.763449Q518.8595 19.00411 518.9817 19.230186L518.3061 19.230186Q518.2055 19.02599 518.17676 18.748863ZM518.12646 17.283012Q517.7743 17.428867 517.07 17.530968Q516.66754 17.58931 516.50226 17.662237Q516.337 17.735165 516.2435 17.881021Q516.1573 18.019585 516.1573 18.187319Q516.1573 18.449858 516.3513 18.624886Q516.55255 18.799911 516.9263 18.799911Q517.3 18.799911 517.58746 18.639471Q517.8821 18.471737 518.0187 18.180025Q518.12646 17.96124 518.12646 17.523674L518.12646 17.283012ZM519.7686 19.230186L519.7686 13.884573L520.4154 13.884573L520.4154 19.230186L519.7686 19.230186ZM521.15924 18.070633L521.7988 17.968534Q521.8492 18.362345 522.0935 18.573835Q522.34503 18.778034 522.78345 18.778034Q523.229 18.778034 523.4446 18.595715Q523.6602 18.406101 523.6602 18.158148Q523.6602 17.939363 523.4733 17.808094Q523.3368 17.72058 522.8122 17.58931Q522.1007 17.40699 521.82043 17.275719Q521.5473 17.137157 521.40356 16.903788Q521.267 16.670418 521.267 16.386Q521.267 16.123459 521.382 15.9046755Q521.497 15.685892 521.69824 15.540036Q521.8492 15.423351 522.1079 15.350424Q522.3738 15.270203 522.66846 15.270203Q523.1212 15.270203 523.459 15.401473Q523.79675 15.532743 523.9549 15.75882Q524.1202 15.977603 524.1848 16.356829L523.5524 16.444342Q523.5093 16.145338 523.3009 15.977603Q523.09247 15.809869 522.71875 15.809869Q522.2732 15.809869 522.07916 15.963017Q521.8923 16.108873 521.8923 16.305779Q521.8923 16.43705 521.9713 16.539148Q522.0504 16.641247 522.2157 16.714174Q522.3163 16.750639 522.79065 16.881908Q523.4733 17.064228 523.74646 17.188206Q524.01953 17.304892 524.1705 17.530968Q524.32855 17.757044 524.32855 18.092512Q524.32855 18.420687 524.1345 18.712399Q523.9477 18.996819 523.5883 19.157259Q523.2362 19.3177 522.79065 19.3177Q522.0432 19.3177 521.65515 19.00411Q521.267 18.690521 521.15924 18.070633ZM527.7063 17.98312L528.3747 18.063341Q528.21655 18.66135 527.7854 18.989525Q527.3614 19.3177 526.7002 19.3177Q525.8593 19.3177 525.37067 18.79262Q524.88196 18.267538 524.88196 17.326769Q524.88196 16.349535 525.3778 15.809869Q525.8737 15.270203 526.66425 15.270203Q527.426 15.270203 527.90753 15.802576Q528.39624 16.327658 528.39624 17.283012Q528.39624 17.341354 528.39624 17.45804L525.5503 17.45804Q525.58624 18.099804 525.90247 18.442566Q526.2259 18.778034 526.7002 18.778034Q527.0595 18.778034 527.31104 18.58842Q527.56256 18.39881 527.7063 17.98312ZM525.58624 16.918373L527.7135 16.918373Q527.6704 16.429756 527.4692 16.189095Q527.16016 15.809869 526.67145 15.809869Q526.2259 15.809869 525.9168 16.116167Q525.615 16.41517 525.58624 16.918373Z" fill-rule="nonzero"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M354.2683 9.362945L368.8418 9.362945L368.8418 23.637056L354.2683 23.637056Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.46333110432708835,1.389993312981265" clip-path="url(#id_0)" d="M354.2683 9.362945L368.8418 9.362945L368.8418 23.637056L354.2683 23.637056Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M361.87903 19.175577Q361.45502 19.532923 361.06693 19.686071Q360.68604 19.831928 360.24048 19.831928Q359.50742 19.831928 359.11215 19.467289Q358.7169 19.10265 358.7169 18.541105Q358.7169 18.205637 358.8678 17.935802Q359.01874 17.658676 359.2559 17.498236Q359.50024 17.330502 359.80206 17.242989Q360.01767 17.184647 360.47043 17.126303Q361.38315 17.016912 361.81436 16.863764Q361.82153 16.703321 361.82153 16.666859Q361.82153 16.200119 361.60593 16.003214Q361.31848 15.747967 360.74353 15.747967Q360.21173 15.747967 359.96017 15.937579Q359.70865 16.127192 359.5865 16.601223L358.84625 16.499125Q358.94687 16.025093 359.17685 15.733381Q359.4068 15.434377 359.84518 15.273935Q360.2836 15.113494 360.85135 15.113494Q361.42627 15.113494 361.7784 15.252057Q362.13776 15.3833275 362.30304 15.594818Q362.47552 15.799016 362.54022 16.112606Q362.57614 16.309511 362.57614 16.820007L362.57614 17.840996Q362.57614 18.905745 362.62643 19.190163Q362.67676 19.467289 362.8133 19.72983L362.02997 19.72983Q361.90778 19.489166 361.87903 19.175577ZM361.81436 17.461771Q361.40472 17.636799 360.58542 17.753483Q360.1183 17.819118 359.92426 17.906631Q359.73022 17.994144 359.6224 18.154587Q359.5218 18.315027 359.5218 18.519226Q359.5218 18.825523 359.75177 19.02972Q359.98175 19.23392 360.42014 19.23392Q360.85135 19.23392 361.18912 19.044308Q361.53406 18.8474 361.69217 18.511932Q361.81436 18.249393 361.81436 17.746191L361.81436 17.461771Z" fill-rule="nonzero"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M453.81924 9.353145L468.39273 9.353145L468.39273 23.627254L453.81924 23.627254Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.46333110432708835,1.389993312981265" clip-path="url(#id_0)" d="M453.81924 9.353145L468.39273 9.353145L468.39273 23.627254L453.81924 23.627254Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M459.21646 19.720028L458.51935 19.720028L458.51935 13.4846945L459.27396 13.4846945L459.27396 15.708995Q459.75546 15.103694 460.4957 15.103694Q460.90533 15.103694 461.27185 15.271428Q461.63837 15.439162 461.87555 15.745459Q462.1127 16.044464 462.24207 16.474737Q462.3786 16.905012 462.3786 17.39363Q462.3786 18.553183 461.81085 19.187654Q461.2503 19.822128 460.45258 19.822128Q459.66922 19.822128 459.21646 19.151192L459.21646 19.720028ZM459.2093 17.430094Q459.2093 18.239594 459.43207 18.604233Q459.7842 19.194948 460.39508 19.194948Q460.89096 19.194948 461.2503 18.757381Q461.60962 18.319813 461.60962 17.459265Q461.60962 16.576838 461.26468 16.153854Q460.9197 15.730873 460.42383 15.730873Q459.93512 15.730873 459.5686 16.16844Q459.2093 16.606009 459.2093 17.430094Z" fill-rule="nonzero"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M404.04434 9.353145L418.61783 9.353145L418.61783 23.627254L404.04434 23.627254Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.46333110432708835,1.389993312981265" clip-path="url(#id_0)" d="M404.04434 9.353145L418.61783 9.353145L418.61783 23.627254L404.04434 23.627254Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M409.44156 19.720028L408.74445 19.720028L408.74445 13.4846945L409.49905 13.4846945L409.49905 15.708995Q409.98056 15.103694 410.72076 15.103694Q411.13043 15.103694 411.49695 15.271428Q411.86346 15.439162 412.10062 15.745459Q412.3378 16.044464 412.46713 16.474737Q412.6037 16.905012 412.6037 17.39363Q412.6037 18.553183 412.03595 19.187654Q411.47537 19.822128 410.67767 19.822128Q409.89432 19.822128 409.44156 19.151192L409.44156 19.720028ZM409.43436 17.430094Q409.43436 18.239594 409.65714 18.604233Q410.0093 19.194948 410.62018 19.194948Q411.11606 19.194948 411.47537 18.757381Q411.83472 18.319813 411.83472 17.459265Q411.83472 16.576838 411.48975 16.153854Q411.1448 15.730873 410.64893 15.730873Q410.16022 15.730873 409.7937 16.16844Q409.43436 16.606009 409.43436 17.430094Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M567.7071 9.353145L582.2806 9.353145L582.2806 23.627254L567.7071 23.627254Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.46333110432708835" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.46333110432708835,1.389993312981265" clip-path="url(#id_0)" d="M567.7071 9.353145L582.2806 9.353145L582.2806 23.627254L567.7071 23.627254Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M575.29626 19.720028L575.29626 19.151192Q574.87946 19.822128 574.053 19.822128Q573.5212 19.822128 573.07556 19.523123Q572.63 19.22412 572.3857 18.691746Q572.1413 18.159372 572.1413 17.466557Q572.1413 16.788328 572.3641 16.24137Q572.58685 15.687117 573.02527 15.395405Q573.4708 15.103694 574.02423 15.103694Q574.4267 15.103694 574.7357 15.278721Q575.05194 15.446455 575.246 15.72358L575.246 13.4846945L576.00055 13.4846945L576.00055 19.720028L575.29626 19.720028ZM572.9175 17.466557Q572.9175 18.334398 573.2768 18.764673Q573.63617 19.194948 574.1248 19.194948Q574.6207 19.194948 574.9657 18.786552Q575.31067 18.370863 575.31067 17.532192Q575.31067 16.606009 574.9585 16.175734Q574.6063 15.738166 574.0889 15.738166Q573.593 15.738166 573.25525 16.153854Q572.9175 16.569544 572.9175 17.466557Z" fill-rule="nonzero"/></svg>
\ No newline at end of file
diff --git a/compose/runtime/design/images/call-tree-5.svg b/compose/runtime/design/images/call-tree-5.svg
new file mode 100644
index 0000000..a74c007
--- /dev/null
+++ b/compose/runtime/design/images/call-tree-5.svg
@@ -0,0 +1 @@
+<svg version="1.1" viewBox="0.0 0.0 437.0 122.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" preserveAspectRatio="none" width="437" height="122" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="id_0"><path d="M0 0L437.0 0L437.0 122.0L0 122.0L0 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M-7.0799766 -17.654459L719.8175 -17.654459L719.8175 527.9556L-7.0799766 527.9556Z" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M172.84384 0.28491777L237.80672 0.28491777L237.80672 30.429516L172.84384 30.429516Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M172.84384 0.28491777L237.80672 0.28491777L237.80672 30.429516L172.84384 30.429516Z" fill-rule="evenodd"/><clipPath id="id_1"><path d="M-2.842171E-14 0L437.0 0L437.0 122.0L-2.842171E-14 122.0L-2.842171E-14 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_1)" d="M194.96977 19.290155L194.96977 11.697436L198.33275 11.697436Q199.3443 11.697436 199.86781 11.901685Q200.4002 12.105933 200.71078 12.629875Q201.03021 13.144937 201.03021 13.775443Q201.03021 14.5746765 200.5067 15.13414Q199.98317 15.684724 198.89175 15.83569Q199.29106 16.031057 199.50401 16.217546Q199.93881 16.617163 200.32922 17.221027L201.65135 19.290155L200.39134 19.290155L199.38866 17.709448Q198.945 17.02566 198.66106 16.670444Q198.3771 16.306349 198.14641 16.164263Q197.92458 16.013298 197.69386 15.960015Q197.52528 15.915613 197.13486 15.915613L195.97246 15.915613L195.97246 19.290155L194.96977 19.290155ZM195.97246 15.054217L198.12866 15.054217Q198.82077 15.054217 199.20233 14.912131Q199.59274 14.761165 199.79683 14.450352Q200.00092 14.139539 200.00092 13.775443Q200.00092 13.233741 199.61049 12.887406Q199.22008 12.541072 198.36824 12.541072L195.97246 12.541072L195.97246 15.054217ZM202.14175 16.53724Q202.14175 15.009815 202.98471 14.281625Q203.69458 13.668879 204.715 13.668879Q205.85078 13.668879 206.56952 14.41483Q207.28825 15.151901 207.28825 16.466196Q207.28825 17.52296 206.96881 18.135706Q206.64937 18.739573 206.03712 19.077026Q205.43373 19.41448 204.715 19.41448Q203.56148 19.41448 202.85162 18.677408Q202.14175 17.931458 202.14175 16.53724ZM203.10007 16.53724Q203.10007 17.594004 203.55261 18.126825Q204.014 18.650768 204.715 18.650768Q205.40712 18.650768 205.86853 18.126825Q206.32994 17.594004 206.32994 16.510597Q206.32994 15.489355 205.86853 14.965413Q205.40712 14.441471 204.715 14.441471Q204.014 14.441471 203.55261 14.965413Q203.10007 15.480474 203.10007 16.53724ZM208.03401 16.53724Q208.03401 15.009815 208.87697 14.281625Q209.58684 13.668879 210.60725 13.668879Q211.74303 13.668879 212.46176 14.41483Q213.1805 15.151901 213.1805 16.466196Q213.1805 17.52296 212.86107 18.135706Q212.54163 18.739573 211.92937 19.077026Q211.32599 19.41448 210.60725 19.41448Q209.45374 19.41448 208.74387 18.677408Q208.03401 17.931458 208.03401 16.53724ZM208.99232 16.53724Q208.99232 17.594004 209.44485 18.126825Q209.90627 18.650768 210.60725 18.650768Q211.29938 18.650768 211.76077 18.126825Q212.22218 17.594004 212.22218 16.510597Q212.22218 15.489355 211.76077 14.965413Q211.29938 14.441471 210.60725 14.441471Q209.90627 14.441471 209.44485 14.965413Q208.99232 15.480474 208.99232 16.53724ZM216.30429 18.4554L216.4374 19.281275Q216.04697 19.361198 215.7364 19.361198Q215.23064 19.361198 214.94669 19.201351Q214.67162 19.041504 214.55626 18.783974Q214.44092 18.517563 214.44092 17.682808L214.44092 14.512514L213.75768 14.512514L213.75768 13.793204L214.44092 13.793204L214.44092 12.434507L215.36372 11.875044L215.36372 13.793204L216.30429 13.793204L216.30429 14.512514L215.36372 14.512514L215.36372 17.73609Q215.36372 18.126825 215.4081 18.242271Q215.46133 18.357716 215.56781 18.428759Q215.68317 18.490921 215.89613 18.490921Q216.04697 18.490921 216.30429 18.4554Z" fill-rule="nonzero"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M70.334236 44.048885L101.06283 44.048885L101.06283 74.19348L70.334236 74.19348Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M70.334236 44.048885L101.06283 44.048885L101.06283 74.19348L70.334236 74.19348Z" fill-rule="evenodd"/><clipPath id="id_2"><path d="M0 7.1054274E-15L437.0 7.1054274E-15L437.0 122.0L0 122.0L0 7.1054274E-15Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_2)" d="M82.1475 63.054123L85.05793 55.461403L86.14047 55.461403L89.24611 63.054123L88.10146 63.054123L87.22301 60.754105L84.04638 60.754105L83.212296 63.054123L82.1475 63.054123ZM84.339195 59.93711L86.912445 59.93711L86.11385 57.832462Q85.75892 56.873383 85.58145 56.260635Q85.43061 56.988827 85.17329 57.708138L84.339195 59.93711Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M205.32529 30.429516L85.701996 44.05187" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M205.32529 30.429516L85.701996 44.05187" fill-rule="evenodd"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M0.28468958 91.56601L31.013279 91.56601L31.013279 121.71061L0.28468958 121.71061Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M0.28468958 91.56601L31.013279 91.56601L31.013279 121.71061L0.28468958 121.71061Z" fill-rule="evenodd"/><clipPath id="id_3"><path d="M-5.551115E-17 0L437.0 0L437.0 122.0L-5.551115E-17 122.0L-5.551115E-17 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_3)" d="M12.887676 110.57124L12.887676 102.97852L15.735992 102.97852Q16.605572 102.97852 17.129095 103.20942Q17.652617 103.44031 17.945435 103.919846Q18.247126 104.39938 18.247126 104.92333Q18.247126 105.41175 17.980927 105.846886Q17.723602 106.27315 17.191208 106.53068Q17.874449 106.734924 18.247126 107.22334Q18.619802 107.71177 18.619802 108.36891Q18.619802 108.90173 18.389097 109.36352Q18.167265 109.816414 17.830082 110.06506Q17.501772 110.31371 17.00487 110.44692Q16.507965 110.57124 15.780358 110.57124L12.887676 110.57124ZM13.899227 106.16658L15.531907 106.16658Q16.206276 106.16658 16.49022 106.086655Q16.871769 105.971214 17.066982 105.7048Q17.262194 105.43839 17.262194 105.03877Q17.262194 104.665794 17.075855 104.38162Q16.89839 104.08858 16.561205 103.98201Q16.224022 103.87544 15.407681 103.87544L13.899227 103.87544L13.899227 106.16658ZM13.899227 109.674324L15.780358 109.674324Q16.268389 109.674324 16.4636 109.63881Q16.809656 109.576645 17.040361 109.434555Q17.27994 109.28359 17.430784 109.0083Q17.58163 108.73301 17.58163 108.36891Q17.58163 107.94266 17.359798 107.63184Q17.146841 107.32103 16.756418 107.1967Q16.374866 107.0635 15.64726 107.0635L13.899227 107.0635L13.899227 109.674324Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M69.96954 91.56601L100.698135 91.56601L100.698135 121.71061L69.96954 121.71061Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M69.96954 91.56601L100.698135 91.56601L100.698135 121.71061L69.96954 121.71061Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M82.57253 110.57124L82.57253 102.97852L85.420845 102.97852Q86.29043 102.97852 86.81395 103.20942Q87.33747 103.44031 87.63029 103.919846Q87.931984 104.39938 87.931984 104.92333Q87.931984 105.41175 87.66578 105.846886Q87.408455 106.27315 86.87606 106.53068Q87.5593 106.734924 87.931984 107.22334Q88.30466 107.71177 88.30466 108.36891Q88.30466 108.90173 88.07395 109.36352Q87.85212 109.816414 87.51494 110.06506Q87.18663 110.31371 86.68972 110.44692Q86.19282 110.57124 85.46522 110.57124L82.57253 110.57124ZM83.58408 106.16658L85.21677 106.16658Q85.89113 106.16658 86.17507 106.086655Q86.556625 105.971214 86.75184 105.7048Q86.94705 105.43839 86.94705 105.03877Q86.94705 104.665794 86.76071 104.38162Q86.583244 104.08858 86.24606 103.98201Q85.908875 103.87544 85.09254 103.87544L83.58408 103.87544L83.58408 106.16658ZM83.58408 109.674324L85.46522 109.674324Q85.95325 109.674324 86.14845 109.63881Q86.494514 109.576645 86.72522 109.434555Q86.9648 109.28359 87.11564 109.0083Q87.26649 108.73301 87.26649 108.36891Q87.26649 107.94266 87.044655 107.63184Q86.831696 107.32103 86.44127 107.1967Q86.05972 107.0635 85.332115 107.0635L83.58408 107.0635L83.58408 109.674324Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M142.1145 91.56601L172.8431 91.56601L172.8431 121.71061L142.1145 121.71061Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M142.1145 91.56601L172.8431 91.56601L172.8431 121.71061L142.1145 121.71061Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M159.88226 107.907135L160.88493 108.164665Q160.5655 109.39903 159.74916 110.05618Q158.93283 110.70445 157.7438 110.70445Q156.5193 110.70445 155.74733 110.20715Q154.98422 109.700966 154.57605 108.75965Q154.17676 107.80945 154.17676 106.71716Q154.17676 105.53607 154.62929 104.65691Q155.08183 103.76888 155.91591 103.31598Q156.75888 102.8542 157.76155 102.8542Q158.89732 102.8542 159.66931 103.43143Q160.45015 104.00865 160.75185 105.065414L159.7669 105.2963Q159.50072 104.47043 158.99493 104.09745Q158.49803 103.7156 157.7438 103.7156Q156.86536 103.7156 156.27972 104.13297Q155.69408 104.550354 155.4545 105.26078Q155.21492 105.96233 155.21492 106.71716Q155.21492 107.68513 155.49887 108.404434Q155.78282 109.12374 156.37732 109.48784Q156.97183 109.843056 157.66394 109.843056Q158.50691 109.843056 159.09254 109.35464Q159.67818 108.86621 159.88226 107.907135Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M85.69853 74.19348L15.655951 91.57496" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M85.69853 74.19348L15.655951 91.57496" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M85.69853 74.19348L85.340805 91.57496" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M85.69853 74.19348L85.340805 91.57496" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M85.69853 74.19348L157.47607 91.57496" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M85.69853 74.19348L157.47607 91.57496" fill-rule="evenodd"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M110.5023 50.43343L128.49586 50.43343L128.49586 67.8149L110.5023 67.8149Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M110.5023 50.43343L128.49586 50.43343L128.49586 67.8149L110.5023 67.8149Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M119.89908 62.3822Q119.37556 62.817333 118.8964 63.003822Q118.42612 63.18143 117.87598 63.18143Q116.9709 63.18143 116.48287 62.73741Q115.99484 62.293392 115.99484 61.609604Q115.99484 61.201107 116.18118 60.87253Q116.36752 60.53508 116.66034 60.33971Q116.96203 60.135464 117.33471 60.028896Q117.60091 59.957855 118.15992 59.88681Q119.28683 59.753605 119.81922 59.56712Q119.828094 59.37175 119.828094 59.327347Q119.828094 58.759007 119.5619 58.519234Q119.20696 58.20842 118.4971 58.20842Q117.840485 58.20842 117.52992 58.439312Q117.21935 58.6702 117.06851 59.247425L116.154564 59.1231Q116.278786 58.545876 116.56274 58.190662Q116.84668 57.826565 117.38795 57.6312Q117.929214 57.43583 118.6302 57.43583Q119.340065 57.43583 119.77486 57.604557Q120.21851 57.764404 120.4226 58.021935Q120.63556 58.270584 120.715416 58.65244Q120.75979 58.892212 120.75979 59.513836L120.75979 60.757088Q120.75979 62.053623 120.8219 62.399956Q120.88401 62.73741 121.052605 63.057106L120.08542 63.057106Q119.93457 62.764053 119.89908 62.3822ZM119.81922 60.29531Q119.313446 60.50844 118.301895 60.650524Q117.72513 60.730446 117.48555 60.837013Q117.24597 60.943577 117.11288 61.138943Q116.98865 61.334312 116.98865 61.582962Q116.98865 61.95594 117.2726 62.20459Q117.55654 62.45324 118.09781 62.45324Q118.6302 62.45324 119.04725 62.22235Q119.47316 61.98258 119.66837 61.57408Q119.81922 61.25439 119.81922 60.641644L119.81922 60.29531Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M101.06283 59.121185L110.50677 59.121185" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M101.06283 59.121185L110.50677 59.121185" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M344.27615 65.50573L356.04532 65.50573" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M344.27615 65.50573L356.04532 65.50573" fill-rule="evenodd"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M42.004738 97.94757L59.998314 97.94757L59.998314 115.32905L42.004738 115.32905Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M42.004738 97.94757L59.998314 97.94757L59.998314 115.32905L42.004738 115.32905Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M48.668556 110.57124L47.80785 110.57124L47.80785 102.97853L48.739544 102.97853L48.739544 105.68704Q49.334053 104.94997 50.247997 104.94997Q50.753773 104.94997 51.20631 105.15422Q51.658848 105.35847 51.951664 105.731445Q52.24448 106.095535 52.4042 106.61948Q52.572792 107.143425 52.572792 107.7384Q52.572792 109.15038 51.871803 109.92298Q51.17969 110.69557 50.19476 110.69557Q49.227573 110.69557 48.668556 109.87858L48.668556 110.57124ZM48.659683 107.78281Q48.659683 108.76853 48.934757 109.21255Q49.369545 109.93186 50.12377 109.93186Q50.736027 109.93186 51.17969 109.39904Q51.623352 108.86621 51.623352 107.81833Q51.623352 106.743805 51.197437 106.228745Q50.77152 105.713684 50.159264 105.713684Q49.555885 105.713684 49.103348 106.246506Q48.659683 106.77933 48.659683 107.78281Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M31.013279 106.638306L42.013325 106.638306" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M31.013279 106.638306L42.013325 106.638306" fill-rule="evenodd"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M112.752365 97.94757L130.74594 97.94757L130.74594 115.32905L112.752365 115.32905Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M112.752365 97.94757L130.74594 97.94757L130.74594 115.32905L112.752365 115.32905Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M119.41618 110.57124L118.55548 110.57124L118.55548 102.97853L119.487175 102.97853L119.487175 105.68704Q120.08168 104.94997 120.99563 104.94997Q121.5014 104.94997 121.95394 105.15422Q122.40648 105.35847 122.699295 105.731445Q122.99211 106.095535 123.15183 106.61948Q123.32042 107.143425 123.32042 107.7384Q123.32042 109.15038 122.61943 109.92298Q121.92732 110.69557 120.94239 110.69557Q119.975204 110.69557 119.41618 109.87858L119.41618 110.57124ZM119.40731 107.78281Q119.40731 108.76853 119.68239 109.21255Q120.11717 109.93186 120.8714 109.93186Q121.48366 109.93186 121.92732 109.39904Q122.37098 108.86621 122.37098 107.81833Q122.37098 106.743805 121.94507 106.228745Q121.51915 105.713684 120.9069 105.713684Q120.30351 105.713684 119.850975 106.246506Q119.40731 106.77933 119.40731 107.78281Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M100.698135 106.638306L112.75347 106.638306" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M100.698135 106.638306L112.75347 106.638306" fill-rule="evenodd"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M184.75777 97.94757L202.75134 97.94757L202.75134 115.32905L184.75777 115.32905Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M184.75777 97.94757L202.75134 97.94757L202.75134 115.32905L184.75777 115.32905Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M194.15456 108.555405L195.0685 108.679726Q194.91765 109.62105 194.29652 110.16275Q193.68427 110.69557 192.7792 110.69557Q191.6523 110.69557 190.96906 109.9585Q190.28581 109.22143 190.28581 107.84497Q190.28581 106.94805 190.57863 106.28203Q190.87144 105.616 191.47482 105.28742Q192.07822 104.94997 192.78807 104.94997Q193.68427 104.94997 194.25217 105.40287Q194.82005 105.85577 194.97977 106.69052L194.07469 106.82373Q193.94159 106.27315 193.61328 105.997856Q193.28497 105.713684 192.82356 105.713684Q192.12257 105.713684 191.67891 106.219864Q191.23524 106.72604 191.23524 107.81833Q191.23524 108.928375 191.66116 109.434555Q192.08708 109.93186 192.77032 109.93186Q193.32047 109.93186 193.68427 109.594406Q194.05695 109.25695 194.15456 108.555405Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M172.8431 106.638306L184.75534 106.638306" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M172.8431 106.638306L184.75534 106.638306" fill-rule="evenodd"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M304.2984 44.05336L335.02698 44.05336L335.02698 74.19796L304.2984 74.19796Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M304.2984 44.05336L335.02698 44.05336L335.02698 74.19796L304.2984 74.19796Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M316.11166 63.058598L319.0221 55.465878L320.1046 55.465878L323.21027 63.058598L322.0656 63.058598L321.18716 60.75858L318.01053 60.75858L317.17645 63.058598L316.11166 63.058598ZM318.30334 59.941586L320.87662 59.941586L320.078 57.836937Q319.72308 56.877857 319.54562 56.26511Q319.39478 56.9933 319.13745 57.712612L318.30334 59.941586Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M234.24886 91.57048L264.97745 91.57048L264.97745 121.71508L234.24886 121.71508Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M234.24886 91.57048L264.97745 91.57048L264.97745 121.71508L234.24886 121.71508Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M246.85184 110.57572L246.85184 102.983L249.70015 102.983Q250.56973 102.983 251.09325 103.21389Q251.61678 103.44478 251.90959 103.924324Q252.21129 104.40386 252.21129 104.9278Q252.21129 105.41622 251.94508 105.851364Q251.68776 106.27762 251.15536 106.53515Q251.83861 106.7394 252.21129 107.22782Q252.58395 107.71624 252.58395 108.37339Q252.58395 108.90621 252.35326 109.36799Q252.13142 109.82089 251.79424 110.06954Q251.46593 110.31819 250.96902 110.45139Q250.47212 110.57572 249.74452 110.57572L246.85184 110.57572ZM247.86339 106.17106L249.49606 106.17106Q250.17044 106.17106 250.45438 106.09113Q250.83592 105.975685 251.03114 105.709274Q251.22635 105.44286 251.22635 105.04325Q251.22635 104.67027 251.04001 104.3861Q250.86255 104.09305 250.52536 103.98648Q250.18819 103.87992 249.37184 103.87992L247.86339 103.87992L247.86339 106.17106ZM247.86339 109.6788L249.74452 109.6788Q250.23254 109.6788 250.42776 109.64328Q250.77382 109.581116 251.00452 109.43903Q251.2441 109.28806 251.39494 109.01277Q251.54579 108.73748 251.54579 108.37339Q251.54579 107.94713 251.32396 107.636314Q251.111 107.3255 250.72058 107.20118Q250.33902 107.06797 249.61142 107.06797L247.86339 107.06797L247.86339 109.6788Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M303.93372 91.57048L334.6623 91.57048L334.6623 121.71508L303.93372 121.71508Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M303.93372 91.57048L334.6623 91.57048L334.6623 121.71508L303.93372 121.71508Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M316.53668 110.57572L316.53668 102.983L319.385 102.983Q320.25458 102.983 320.7781 103.21389Q321.30164 103.44478 321.59445 103.924324Q321.89615 104.40386 321.89615 104.9278Q321.89615 105.41622 321.62994 105.851364Q321.37262 106.27762 320.8402 106.53515Q321.52347 106.7394 321.89615 107.22782Q322.26883 107.71624 322.26883 108.37339Q322.26883 108.90621 322.03812 109.36799Q321.81628 109.82089 321.4791 110.06954Q321.1508 110.31819 320.65387 110.45139Q320.15698 110.57572 319.42938 110.57572L316.53668 110.57572ZM317.54825 106.17106L319.1809 106.17106Q319.8553 106.17106 320.13922 106.09113Q320.52078 105.975685 320.716 105.709274Q320.9112 105.44286 320.9112 105.04325Q320.9112 104.67027 320.72488 104.3861Q320.5474 104.09305 320.2102 103.98648Q319.87305 103.87992 319.0567 103.87992L317.54825 103.87992L317.54825 106.17106ZM317.54825 109.6788L319.42938 109.6788Q319.9174 109.6788 320.1126 109.64328Q320.45868 109.581116 320.68936 109.43903Q320.92896 109.28806 321.0798 109.01277Q321.23065 108.73748 321.23065 108.37339Q321.23065 107.94713 321.00882 107.636314Q320.79587 107.3255 320.40543 107.20118Q320.0239 107.06797 319.29626 107.06797L317.54825 107.06797L317.54825 109.6788Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M376.07867 91.57048L406.80725 91.57048L406.80725 121.71508L376.07867 121.71508Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M376.07867 91.57048L406.80725 91.57048L406.80725 121.71508L376.07867 121.71508Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M388.43375 110.57572L388.43375 102.983L391.05133 102.983Q391.9298 102.983 392.4001 103.09845Q393.04782 103.24053 393.50925 103.64015Q394.1126 104.14633 394.40543 104.93668Q394.70712 105.727036 394.70712 106.7394Q394.70712 107.6008 394.50305 108.2757Q394.29895 108.941734 393.97952 109.37687Q393.66895 109.81201 393.29626 110.06954Q392.9236 110.31819 392.3912 110.45139Q391.85883 110.57572 391.17557 110.57572L388.43375 110.57572ZM389.4364 109.6788L391.0602 109.6788Q391.80557 109.6788 392.23148 109.5456Q392.6574 109.40351 392.91473 109.14598Q393.26965 108.790764 393.46487 108.1869Q393.66895 107.58304 393.66895 106.72164Q393.66895 105.53167 393.27853 104.89228Q392.88812 104.2529 392.3291 104.039764Q391.9298 103.87992 391.0336 103.87992L389.4364 103.87992L389.4364 109.6788Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M319.6627 74.19796L249.62012 91.57944" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M319.6627 74.19796L249.62012 91.57944" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M319.6627 74.19796L319.30496 91.57944" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M319.6627 74.19796L319.30496 91.57944" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M319.6627 74.19796L391.44025 91.57944" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M319.6627 74.19796L391.44025 91.57944" fill-rule="evenodd"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M344.46646 50.437904L362.46002 50.437904L362.46002 67.81938L344.46646 67.81938Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M344.46646 50.437904L362.46002 50.437904L362.46002 67.81938L344.46646 67.81938Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M353.86325 62.386673Q353.33972 62.82181 352.86057 63.008297Q352.3903 63.185905 351.84015 63.185905Q350.93506 63.185905 350.44702 62.741886Q349.959 62.297867 349.959 61.61408Q349.959 61.20558 350.14536 60.87701Q350.3317 60.539555 350.6245 60.344185Q350.92618 60.13994 351.29886 60.033375Q351.56506 59.96233 352.12408 59.89129Q353.25098 59.758083 353.7834 59.571594Q353.79227 59.376225 353.79227 59.331825Q353.79227 58.76348 353.52606 58.52371Q353.1711 58.2129 352.46127 58.2129Q351.80466 58.2129 351.49408 58.443787Q351.1835 58.674675 351.03265 59.2519L350.1187 59.127575Q350.24295 58.55035 350.5269 58.195137Q350.81085 57.83104 351.3521 57.635674Q351.89337 57.440304 352.59436 57.440304Q353.30423 57.440304 353.739 57.60903Q354.18268 57.76888 354.38675 58.02641Q354.59973 58.27506 354.67957 58.656914Q354.72394 58.896687 354.72394 59.51831L354.72394 60.761562Q354.72394 62.058098 354.78607 62.404434Q354.84818 62.741886 355.01675 63.06158L354.0496 63.06158Q353.89874 62.768528 353.86325 62.386673ZM353.7834 60.299786Q353.27762 60.512913 352.26605 60.655Q351.6893 60.734924 351.4497 60.841488Q351.21014 60.94805 351.07703 61.14342Q350.95282 61.338787 350.95282 61.587437Q350.95282 61.960415 351.23676 62.209064Q351.5207 62.457714 352.06198 62.457714Q352.59436 62.457714 353.0114 62.226826Q353.43732 61.987057 353.63254 61.578556Q353.7834 61.258865 353.7834 60.64612L353.7834 60.299786Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M335.02698 59.12566L344.47092 59.12566" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M335.02698 59.12566L344.47092 59.12566" fill-rule="evenodd"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M275.9689 97.95204L293.96246 97.95204L293.96246 115.33352L275.9689 115.33352Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M275.9689 97.95204L293.96246 97.95204L293.96246 115.33352L275.9689 115.33352Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M282.63272 110.57572L281.772 110.57572L281.772 102.983L282.7037 102.983L282.7037 105.69151Q283.29822 104.954445 284.21216 104.954445Q284.71793 104.954445 285.17047 105.15869Q285.62302 105.362946 285.91583 105.735916Q286.20865 106.10001 286.36835 106.623955Q286.53696 107.147896 286.53696 107.74288Q286.53696 109.15486 285.83597 109.92745Q285.14386 110.70004 284.1589 110.70004Q283.19174 110.70004 282.63272 109.88305L282.63272 110.57572ZM282.62384 107.787285Q282.62384 108.773 282.89893 109.217026Q283.3337 109.93633 284.08792 109.93633Q284.7002 109.93633 285.14386 109.40351Q285.58752 108.87069 285.58752 107.82281Q285.58752 106.74828 285.1616 106.233215Q284.7357 105.718155 284.1234 105.718155Q283.52005 105.718155 283.0675 106.25098Q282.62384 106.7838 282.62384 107.787285Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M264.97745 106.642784L275.97748 106.642784" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M264.97745 106.642784L275.97748 106.642784" fill-rule="evenodd"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M346.71652 97.95204L364.7101 97.95204L364.7101 115.33352L346.71652 115.33352Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M346.71652 97.95204L364.7101 97.95204L364.7101 115.33352L346.71652 115.33352Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M353.38034 110.57572L352.51965 110.57572L352.51965 102.983L353.45132 102.983L353.45132 105.69151Q354.04584 104.954445 354.95978 104.954445Q355.46558 104.954445 355.9181 105.15869Q356.37064 105.362946 356.66345 105.735916Q356.95627 106.10001 357.116 106.623955Q357.28458 107.147896 357.28458 107.74288Q357.28458 109.15486 356.5836 109.92745Q355.89148 110.70004 354.90656 110.70004Q353.93936 110.70004 353.38034 109.88305L353.38034 110.57572ZM353.37146 107.787285Q353.37146 108.773 353.64655 109.217026Q354.08133 109.93633 354.83557 109.93633Q355.4478 109.93633 355.89148 109.40351Q356.33514 108.87069 356.33514 107.82281Q356.33514 106.74828 355.9092 106.233215Q355.4833 105.718155 354.87106 105.718155Q354.26767 105.718155 353.81512 106.25098Q353.37146 106.7838 353.37146 107.787285Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M334.6623 106.642784L346.71762 106.642784" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M334.6623 106.642784L346.71762 106.642784" fill-rule="evenodd"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M418.72192 97.95204L436.7155 97.95204L436.7155 115.33352L418.72192 115.33352Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M418.72192 97.95204L436.7155 97.95204L436.7155 115.33352L418.72192 115.33352Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M428.0921 110.57572L428.0921 109.88305Q427.57745 110.70004 426.557 110.70004Q425.9004 110.70004 425.35025 110.33595Q424.8001 109.971855 424.4984 109.323586Q424.19675 108.67532 424.19675 107.83169Q424.19675 107.00581 424.4718 106.33978Q424.7469 105.66487 425.28815 105.30966Q425.8383 104.954445 426.5215 104.954445Q427.01843 104.954445 427.4 105.16757Q427.7904 105.371826 428.02997 105.709274L428.02997 102.983L428.96167 102.983L428.96167 110.57572L428.0921 110.57572ZM425.15503 107.83169Q425.15503 108.88845 425.5987 109.41239Q426.04236 109.93633 426.64575 109.93633Q427.258 109.93633 427.68393 109.43903Q428.10983 108.93285 428.10983 107.911606Q428.10983 106.7838 427.67505 106.25986Q427.24026 105.727036 426.60138 105.727036Q425.98914 105.727036 425.57208 106.233215Q425.15503 106.7394 425.15503 107.83169Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M406.80725 106.642784L418.7195 106.642784" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.5681161906120052,1.7043485718360158" clip-path="url(#id_0)" d="M406.80725 106.642784L418.7195 106.642784" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M205.32529 30.429516L319.65424 44.05187" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.5681161906120052" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M205.32529 30.429516L319.65424 44.05187" fill-rule="evenodd"/></svg>
\ No newline at end of file
diff --git a/compose/runtime/design/images/call-tree-6.svg b/compose/runtime/design/images/call-tree-6.svg
new file mode 100644
index 0000000..587b7c3
--- /dev/null
+++ b/compose/runtime/design/images/call-tree-6.svg
@@ -0,0 +1 @@
+<svg version="1.1" viewBox="0.0 0.0 292.0 117.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" preserveAspectRatio="none" width="292" height="117" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="id_0"><path d="M0 0L292.0 0L292.0 117.0L0 117.0L0 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M-70.93239 -13.627931L832.5913 -13.627931L832.5913 665.6514L-70.93239 665.6514Z" fill-rule="evenodd"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M35.14012 0.3547199L259.9761 0.3547199L259.9761 46.70974L35.14012 46.70974Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M35.14012 0.3547199L259.9761 0.3547199L259.9761 46.70974L35.14012 46.70974Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M42.529778 28.428701L42.529778 18.97584L46.7099 18.97584Q47.967243 18.97584 48.617977 19.230127Q49.279736 19.484413 49.665764 20.136717Q50.06282 20.777964 50.06282 21.562939Q50.06282 22.557976 49.412086 23.254503Q48.761356 23.939974 47.404747 24.127926Q47.90107 24.371157 48.16577 24.603333Q48.70621 25.100851 49.1915 25.852657L50.834873 28.428701L49.268707 28.428701L48.022392 26.460737Q47.470924 25.609426 47.117985 25.167187Q46.765045 24.713892 46.478283 24.536997Q46.20255 24.349045 45.915787 24.28271Q45.70623 24.227428 45.22094 24.227428L43.776093 24.227428L43.776093 28.428701L42.529778 28.428701ZM43.776093 23.154999L46.456226 23.154999Q47.316513 23.154999 47.790775 22.978104Q48.276066 22.790152 48.52974 22.403193Q48.783417 22.016233 48.783417 21.562939Q48.783417 20.888523 48.298126 20.45734Q47.81283 20.026157 46.754017 20.026157L43.776093 20.026157L43.776093 23.154999ZM51.44445 25.001348Q51.44445 23.09972 52.492237 22.193129Q53.374584 21.430265 54.64296 21.430265Q56.054714 21.430265 56.94809 22.358969Q57.84147 23.276615 57.84147 24.912899Q57.84147 26.228561 57.444412 26.991425Q57.047356 27.74323 56.28633 28.163357Q55.536335 28.583485 54.64296 28.583485Q53.209145 28.583485 52.326797 27.665838Q51.44445 26.737137 51.44445 25.001348ZM52.635616 25.001348Q52.635616 26.317009 53.198112 26.980368Q53.77164 27.632671 54.64296 27.632671Q55.503246 27.632671 56.07677 26.980368Q56.6503 26.317009 56.6503 24.96818Q56.6503 23.696743 56.07677 23.04444Q55.503246 22.392136 54.64296 22.392136Q53.77164 22.392136 53.198112 23.04444Q52.635616 23.685686 52.635616 25.001348ZM58.768436 25.001348Q58.768436 23.09972 59.816227 22.193129Q60.698574 21.430265 61.966946 21.430265Q63.378704 21.430265 64.27208 22.358969Q65.16546 23.276615 65.16546 24.912899Q65.16546 26.228561 64.7684 26.991425Q64.371346 27.74323 63.61032 28.163357Q62.860325 28.583485 61.966946 28.583485Q60.533134 28.583485 59.650787 27.665838Q58.768436 26.737137 58.768436 25.001348ZM59.959606 25.001348Q59.959606 26.317009 60.522102 26.980368Q61.09563 27.632671 61.966946 27.632671Q62.827236 27.632671 63.40076 26.980368Q63.97429 26.317009 63.97429 24.96818Q63.97429 23.696743 63.40076 23.04444Q62.827236 22.392136 61.966946 22.392136Q61.09563 22.392136 60.522102 23.04444Q59.959606 23.685686 59.959606 25.001348ZM69.048294 27.389439L69.21373 28.417645Q68.72844 28.517149 68.342415 28.517149Q67.71374 28.517149 67.3608 28.318142Q67.01889 28.119133 66.87551 27.79851Q66.73213 27.466831 66.73213 26.427568L66.73213 22.480585L65.88287 22.480585L65.88287 21.58505L66.73213 21.58505L66.73213 19.893486L67.87918 19.196959L67.87918 21.58505L69.048294 21.58505L69.048294 22.480585L67.87918 22.480585L67.87918 26.493904Q67.87918 26.980368 67.934326 27.124096Q68.0005 27.267824 68.13286 27.356272Q68.27624 27.433662 68.54094 27.433662Q68.72844 27.433662 69.048294 27.389439Z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M147.55812 46.70974L62.875008 67.324356" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M147.55812 46.70974L62.875008 67.324356" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" clip-path="url(#id_0)" d="M147.55812 46.70974L229.1287 67.324356" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M147.55812 46.70974L229.1287 67.324356" fill-rule="evenodd"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M82.314156 12.713273L104.679924 12.713273L104.679924 34.353046L82.314156 34.353046Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7067297530918322,2.1201892592754965" clip-path="url(#id_0)" d="M82.314156 12.713273L104.679924 12.713273L104.679924 34.353046L82.314156 34.353046Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M93.99423 27.589375Q93.3435 28.131119 92.74792 28.363295Q92.16336 28.584414 91.47954 28.584414Q90.354546 28.584414 89.74793 28.031614Q89.14132 27.478815 89.14132 26.627504Q89.14132 26.11893 89.37293 25.709858Q89.60455 25.289732 89.96852 25.0465Q90.34351 24.792212 90.80675 24.65954Q91.13763 24.571093 91.83248 24.482645Q93.23321 24.316805 93.894966 24.08463Q93.906 23.841398 93.906 23.786118Q93.906 23.078535 93.57511 22.780024Q93.13394 22.393064 92.251595 22.393064Q91.435425 22.393064 91.04939 22.680521Q90.66337 22.967976 90.47587 23.686615L89.33984 23.531832Q89.494255 22.813192 89.8472 22.370953Q90.200134 21.917658 90.872925 21.674427Q91.545715 21.431194 92.41703 21.431194Q93.29938 21.431194 93.83982 21.641258Q94.39129 21.840265 94.64496 22.160889Q94.90967 22.470457 95.00893 22.945864Q95.06408 23.244375 95.06408 24.018293L95.06408 25.566132Q95.06408 27.180304 95.14128 27.611486Q95.21848 28.031614 95.42805 28.42963L94.225845 28.42963Q94.038345 28.064783 93.99423 27.589375ZM93.894966 24.99122Q93.2663 25.256563 92.00895 25.433458Q91.29204 25.532963 90.99425 25.665634Q90.69646 25.798307 90.53101 26.041538Q90.3766 26.28477 90.3766 26.594337Q90.3766 27.058687 90.729546 27.368256Q91.08248 27.677822 91.75527 27.677822Q92.41703 27.677822 92.93541 27.390368Q93.46482 27.091856 93.707466 26.58328Q93.894966 26.185266 93.894966 25.422403L93.894966 24.99122Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M113.268105 12.711416L135.63388 12.711416L135.63388 34.35119L113.268105 34.35119Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7067297530918322,2.1201892592754965" clip-path="url(#id_0)" d="M113.268105 12.711416L135.63388 12.711416L135.63388 34.35119L113.268105 34.35119Z" fill-rule="evenodd"/><clipPath id="id_1"><path d="M1.4210855E-14 0L292.0 0L292.0 117.0L1.4210855E-14 117.0L1.4210855E-14 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_1)" d="M121.55114 28.427773L120.48129 28.427773L120.48129 18.97491L121.63937 18.97491L121.63937 22.346983Q122.37834 21.429338 123.514366 21.429338Q124.143036 21.429338 124.705536 21.683624Q125.26803 21.937912 125.631996 22.402264Q125.995964 22.855558 126.194496 23.507862Q126.40405 24.160164 126.40405 24.900915Q126.40405 26.658815 125.53273 27.620686Q124.67245 28.582556 123.44819 28.582556Q122.24599 28.582556 121.55114 27.565407L121.55114 28.427773ZM121.540115 24.956196Q121.540115 26.183409 121.88202 26.736208Q122.42246 27.631742 123.359955 27.631742Q124.12098 27.631742 124.67245 26.968384Q125.223915 26.305025 125.223915 25.00042Q125.223915 23.662645 124.6945 23.021399Q124.16509 22.380152 123.40407 22.380152Q122.654076 22.380152 122.091576 23.04351Q121.540115 23.70687 121.540115 24.956196Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M144.2239 12.711416L166.58968 12.711416L166.58968 34.35119L144.2239 34.35119Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7067297530918322,2.1201892592754965" clip-path="url(#id_0)" d="M144.2239 12.711416L166.58968 12.711416L166.58968 34.35119L144.2239 34.35119Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M152.50694 28.427773L151.4371 28.427773L151.4371 18.97491L152.59518 18.97491L152.59518 22.346983Q153.33414 21.429338 154.47017 21.429338Q155.09883 21.429338 155.66133 21.683624Q156.22383 21.937912 156.5878 22.402264Q156.95177 22.855558 157.1503 23.507862Q157.35985 24.160164 157.35985 24.900915Q157.35985 26.658815 156.48854 27.620686Q155.62825 28.582556 154.40399 28.582556Q153.2018 28.582556 152.50694 27.565407L152.50694 28.427773ZM152.49591 24.956196Q152.49591 26.183409 152.83783 26.736208Q153.37827 27.631742 154.31575 27.631742Q155.07678 27.631742 155.62825 26.968384Q156.17972 26.305025 156.17972 25.00042Q156.17972 23.662645 155.6503 23.021399Q155.1209 22.380152 154.35988 22.380152Q153.60988 22.380152 153.04738 23.04351Q152.49591 23.70687 152.49591 24.956196Z" fill-rule="nonzero"/><path fill="#d9ead3" clip-path="url(#id_0)" d="M172.10794 12.712344L194.47371 12.712344L194.47371 34.352116L172.10794 34.352116Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7067297530918322,2.1201892592754965" clip-path="url(#id_0)" d="M172.10794 12.712344L194.47371 12.712344L194.47371 34.352116L172.10794 34.352116Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M183.78801 27.588446Q183.13728 28.13019 182.54169 28.362366Q181.95714 28.583485 181.27332 28.583485Q180.14833 28.583485 179.54172 28.030685Q178.9351 27.477886 178.9351 26.626575Q178.9351 26.118002 179.16672 25.708931Q179.39833 25.288803 179.7623 25.045572Q180.1373 24.791285 180.60052 24.658611Q180.93141 24.570164 181.62625 24.481716Q183.02698 24.315876 183.68875 24.083702Q183.69977 23.84047 183.69977 23.78519Q183.69977 23.077606 183.3689 22.779095Q182.92772 22.392136 182.04538 22.392136Q181.2292 22.392136 180.84317 22.679592Q180.45715 22.967047 180.26965 23.685686L179.13362 23.530903Q179.28804 22.812263 179.64098 22.370024Q179.99391 21.916729 180.6667 21.673498Q181.3395 21.430265 182.21082 21.430265Q183.09315 21.430265 183.63359 21.64033Q184.18506 21.839338 184.43874 22.159962Q184.70345 22.469528 184.8027 22.944935Q184.85785 23.243446 184.85785 24.017365L184.85785 25.565203Q184.85785 27.179375 184.93506 27.610558Q185.01227 28.030685 185.22182 28.428701L184.01962 28.428701Q183.83212 28.063854 183.78801 27.588446ZM183.68875 24.990292Q183.06007 25.255634 181.80273 25.432531Q181.08582 25.532034 180.78802 25.664705Q180.49023 25.797379 180.3248 26.04061Q180.17038 26.28384 180.17038 26.593409Q180.17038 27.05776 180.52333 27.367327Q180.87627 27.676895 181.54906 27.676895Q182.21082 27.676895 182.72919 27.389439Q183.2586 27.090927 183.50125 26.582352Q183.68875 26.184338 183.68875 25.421474L183.68875 24.990292Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M199.99382 12.710487L222.35959 12.710487L222.35959 34.350258L199.99382 34.350258Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7067297530918322,2.1201892592754965" clip-path="url(#id_0)" d="M199.99382 12.710487L222.35959 12.710487L222.35959 34.350258L199.99382 34.350258Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M208.27686 28.426844L207.207 28.426844L207.207 18.973982L208.36508 18.973982L208.36508 22.346056Q209.10405 21.42841 210.24008 21.42841Q210.86874 21.42841 211.43124 21.682697Q211.99374 21.936985 212.35771 22.401335Q212.72168 22.85463 212.92021 23.506933Q213.12976 24.159235 213.12976 24.899986Q213.12976 26.657887 212.25844 27.619757Q211.39816 28.581627 210.1739 28.581627Q208.9717 28.581627 208.27686 27.564478L208.27686 28.426844ZM208.26582 24.955267Q208.26582 26.18248 208.60773 26.73528Q209.14818 27.630814 210.08566 27.630814Q210.8467 27.630814 211.39816 26.967455Q211.94963 26.304096 211.94963 24.99949Q211.94963 23.661716 211.42021 23.02047Q210.89081 22.379223 210.12978 22.379223Q209.37979 22.379223 208.81729 23.042582Q208.26582 23.70594 208.26582 24.955267Z" fill-rule="nonzero"/><path fill="#d0e0e3" clip-path="url(#id_0)" d="M227.87877 12.710487L250.24454 12.710487L250.24454 34.350258L227.87877 34.350258Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7067297530918322,2.1201892592754965" clip-path="url(#id_0)" d="M227.87877 12.710487L250.24454 12.710487L250.24454 34.350258L227.87877 34.350258Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M236.1618 28.426844L235.09196 28.426844L235.09196 18.973982L236.25005 18.973982L236.25005 22.346056Q236.98901 21.42841 238.12503 21.42841Q238.75371 21.42841 239.3162 21.682697Q239.8787 21.936985 240.24266 22.401335Q240.60663 22.85463 240.80516 23.506933Q241.01472 24.159235 241.01472 24.899986Q241.01472 26.657887 240.1434 27.619757Q239.28311 28.581627 238.05885 28.581627Q236.85666 28.581627 236.1618 27.564478L236.1618 28.426844ZM236.15077 24.955267Q236.15077 26.18248 236.49269 26.73528Q237.03313 27.630814 237.97063 27.630814Q238.73164 27.630814 239.28311 26.967455Q239.83458 26.304096 239.83458 24.99949Q239.83458 23.661716 239.30518 23.02047Q238.77576 22.379223 238.01474 22.379223Q237.26474 22.379223 236.70224 23.042582Q236.15077 23.70594 236.15077 24.955267Z" fill-rule="nonzero"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M0.3538653 67.32621L125.41097 67.32621L125.41097 116.64528L0.3538653 116.64528Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M0.3538653 67.32621L125.41097 67.32621L125.41097 116.64528L0.3538653 116.64528Z" fill-rule="evenodd"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M3.03194 73.21345L41.227158 73.21345L41.227158 110.74319L3.03194 110.74319Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M3.03194 73.21345L41.227158 73.21345L41.227158 110.74319L3.03194 110.74319Z" fill-rule="evenodd"/><clipPath id="id_2"><path d="M4.440892E-16 0L292.0 0L292.0 117.0L4.440892E-16 117.0L4.440892E-16 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_2)" d="M19.695026 96.87479L19.695026 87.42193L20.941343 87.42193L20.941343 96.87479L19.695026 96.87479ZM23.276602 96.87479L23.276602 90.926674L22.250872 90.926674L22.250872 90.031136L23.276602 90.031136L23.276602 89.301445Q23.276602 88.615974 23.397924 88.27323Q23.563364 87.81994 23.98248 87.54354Q24.401594 87.26714 25.162619 87.26714Q25.64791 87.26714 26.243494 87.3777L26.067024 88.39485Q25.714087 88.328514 25.394236 88.328514Q24.864826 88.328514 24.64424 88.56069Q24.423653 88.78181 24.423653 89.40095L24.423653 90.031136L25.758204 90.031136L25.758204 90.926674L24.423653 90.926674L24.423653 96.87479L23.276602 96.87479Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M49.143604 73.21345L117.619316 73.21345L117.619316 110.74319L49.143604 110.74319Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M49.143604 73.21345L117.619316 73.21345L117.619316 110.74319L49.143604 110.74319Z" fill-rule="evenodd"/><clipPath id="id_3"><path d="M7.1054274E-15 0L292.0 0L292.0 117.0L7.1054274E-15 117.0L7.1054274E-15 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_3)" d="M58.408253 95.22922L58.551636 96.11369Q58.13252 96.20214 57.80164 96.20214Q57.2612 96.20214 56.96341 96.0363Q56.665615 95.859406 56.544292 95.58301Q56.42297 95.295555 56.42297 94.40002L56.42297 91.02795L55.695034 91.02795L55.695034 90.25403L56.42297 90.25403L56.42297 88.79464L57.41561 88.19762L57.41561 90.25403L58.408253 90.25403L58.408253 91.02795L57.41561 91.02795L57.41561 94.4553Q57.41561 94.88648 57.45973 95.008095Q57.514877 95.129715 57.6362 95.20711Q57.757523 95.27344 57.978107 95.27344Q58.143547 95.27344 58.408253 95.22922ZM59.37332 96.12475L59.37332 90.25403L60.266697 90.25403L60.266697 91.138504Q60.60861 90.51937 60.89537 90.32036Q61.182133 90.12135 61.535072 90.12135Q62.03139 90.12135 62.54977 90.44198L62.207863 91.35963Q61.84389 91.14956 61.479923 91.14956Q61.160072 91.14956 60.89537 91.34857Q60.641693 91.53652 60.531403 91.89031Q60.36596 92.421 60.36596 93.051186L60.36596 96.12475L59.37332 96.12475ZM66.98357 96.12475L66.98357 95.26238Q66.29974 96.25742 65.119606 96.25742Q64.60123 96.25742 64.149025 96.05842Q63.70785 95.859406 63.487267 95.5609Q63.266678 95.25133 63.178444 94.820145Q63.123295 94.52164 63.123295 93.89144L63.123295 90.25403L64.11594 90.25403L64.11594 93.504486Q64.11594 94.28946 64.17108 94.5548Q64.27035 94.95282 64.56814 95.173935Q64.87696 95.39506 65.31814 95.39506Q65.77034 95.39506 66.156364 95.173935Q66.55342 94.941765 66.70783 94.54375Q66.873276 94.14573 66.873276 93.39392L66.873276 90.25403L67.86591 90.25403L67.86591 96.12475L66.98357 96.12475ZM73.430214 94.23418L74.45595 94.3558Q74.2133 95.26238 73.55154 95.7599Q72.90081 96.25742 71.88611 96.25742Q70.59567 96.25742 69.84568 95.461395Q69.09569 94.66536 69.09569 93.23914Q69.09569 91.75764 69.85671 90.9395Q70.61774 90.12135 71.83096 90.12135Q73.00007 90.12135 73.73904 90.928444Q74.48904 91.72447 74.48904 93.172806Q74.48904 93.26125 74.48904 93.43815L70.121414 93.43815Q70.17656 94.41107 70.66185 94.9307Q71.15817 95.43928 71.88611 95.43928Q72.43758 95.43928 72.8236 95.151825Q73.20963 94.86437 73.430214 94.23418ZM70.17656 92.62L73.441246 92.62Q73.37507 91.87926 73.066246 91.514404Q72.59199 90.9395 71.84199 90.9395Q71.15817 90.9395 70.68391 91.40385Q70.22068 91.85714 70.17656 92.62Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M89.66075 81.1575L112.02651 81.1575L112.02651 102.79727L89.66075 102.79727Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7067297530918322,2.1201892592754965" clip-path="url(#id_0)" d="M89.66075 81.1575L112.02651 81.1575L112.02651 102.79727L89.66075 102.79727Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M101.34082 94.36415L102.476845 94.518936Q102.289345 95.69087 101.51729 96.36528Q100.75626 97.02864 99.63127 97.02864Q98.230545 97.02864 97.38129 96.111Q96.53203 95.19335 96.53203 93.479675Q96.53203 92.36302 96.895996 91.53382Q97.259964 90.70462 98.00996 90.29555Q98.75996 89.87543 99.6423 89.87543Q100.75626 89.87543 101.46214 90.43928Q102.16802 91.003136 102.36655 92.0424L101.241554 92.20824Q101.07612 91.522766 100.66803 91.18003Q100.25995 90.82624 99.68642 90.82624Q98.8151 90.82624 98.26363 91.45643Q97.712166 92.08662 97.712166 93.4465Q97.712166 94.82851 98.24158 95.458694Q98.77098 96.07783 99.62025 96.07783Q100.30406 96.07783 100.75626 95.6577Q101.2195 95.23757 101.34082 94.36415Z" fill-rule="nonzero"/><path fill="#cfe2f3" clip-path="url(#id_0)" d="M166.58904 67.32621L291.64615 67.32621L291.64615 116.64528L166.58904 116.64528Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M166.58904 67.32621L291.64615 67.32621L291.64615 116.64528L166.58904 116.64528Z" fill-rule="evenodd"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M169.2671 73.21345L207.46233 73.21345L207.46233 110.74319L169.2671 110.74319Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M169.2671 73.21345L207.46233 73.21345L207.46233 110.74319L169.2671 110.74319Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M185.93019 96.87479L185.93019 87.42193L187.17651 87.42193L187.17651 96.87479L185.93019 96.87479ZM189.51176 96.87479L189.51176 90.926674L188.48604 90.926674L188.48604 90.031136L189.51176 90.031136L189.51176 89.301445Q189.51176 88.615974 189.63309 88.27323Q189.79852 87.81994 190.21765 87.54354Q190.63676 87.26714 191.39778 87.26714Q191.88307 87.26714 192.47865 87.3777L192.30219 88.39485Q191.94925 88.328514 191.6294 88.328514Q191.09999 88.328514 190.87941 88.56069Q190.65881 88.78181 190.65881 89.40095L190.65881 90.031136L191.99336 90.031136L191.99336 90.926674L190.65881 90.926674L190.65881 96.87479L189.51176 96.87479Z" fill-rule="nonzero"/><path fill="#ead1dc" clip-path="url(#id_0)" d="M215.37877 73.21345L283.8545 73.21345L283.8545 110.74319L215.37877 110.74319Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" clip-path="url(#id_0)" d="M215.37877 73.21345L283.8545 73.21345L283.8545 110.74319L215.37877 110.74319Z" fill-rule="evenodd"/><clipPath id="id_4"><path d="M2.842171E-14 0L292.0 0L292.0 117.0L2.842171E-14 117.0L2.842171E-14 0Z" clip-rule="nonzero"/></clipPath><path fill="#000000" clip-path="url(#id_4)" d="M222.71329 96.12475L222.71329 91.02795L221.84196 91.02795L221.84196 90.25403L222.71329 90.25403L222.71329 89.62383Q222.71329 89.03787 222.82358 88.75041Q222.96696 88.36346 223.3199 88.120224Q223.68387 87.87699 224.3346 87.87699Q224.75371 87.87699 225.26106 87.976494L225.11768 88.849915Q224.80885 88.79464 224.53313 88.79464Q224.08092 88.79464 223.89342 88.993645Q223.70592 89.181595 223.70592 89.71228L223.70592 90.25403L224.84195 90.25403L224.84195 91.02795L223.70592 91.02795L223.70592 96.12475L222.71329 96.12475ZM229.43567 95.39506Q228.8842 95.87046 228.37685 96.06947Q227.8695 96.25742 227.28494 96.25742Q226.31436 96.25742 225.79599 95.78201Q225.2776 95.30661 225.2776 94.57691Q225.2776 94.14573 225.47614 93.79194Q225.67467 93.42709 225.98349 93.217026Q226.30333 92.99591 226.7004 92.885345Q226.99818 92.80796 227.58273 92.74162Q228.78494 92.59789 229.35846 92.39889Q229.35846 92.18882 229.35846 92.133545Q229.35846 91.52547 229.08273 91.282234Q228.6967 90.9395 227.9467 90.9395Q227.25186 90.9395 226.90994 91.19379Q226.57907 91.43702 226.42465 92.05615L225.45407 91.92348Q225.58643 91.304344 225.88422 90.91738Q226.19304 90.530426 226.76657 90.33142Q227.34009 90.12135 228.09009 90.12135Q228.84009 90.12135 229.30331 90.29825Q229.77757 90.47514 229.99817 90.740486Q230.21875 91.00584 230.30699 91.4149Q230.36214 91.66919 230.36214 92.33255L230.36214 93.65927Q230.36214 95.05232 230.41728 95.41717Q230.48346 95.78201 230.67096 96.12475L229.6342 96.12475Q229.47978 95.815186 229.43567 95.39506ZM229.35846 93.172806Q228.81802 93.39392 227.73715 93.548706Q227.1195 93.63715 226.86583 93.74772Q226.61215 93.85828 226.46878 94.07939Q226.33643 94.28946 226.33643 94.54375Q226.33643 94.941765 226.63422 95.20711Q226.94304 95.47245 227.51656 95.47245Q228.09009 95.47245 228.53127 95.22922Q228.98346 94.97493 229.19302 94.53269Q229.35846 94.20101 229.35846 93.53765L229.35846 93.172806ZM231.87866 96.12475L231.87866 88.02072L232.8713 88.02072L232.8713 96.12475L231.87866 96.12475ZM234.01285 94.36685L234.99446 94.21207Q235.07166 94.80909 235.44666 95.129715Q235.83269 95.43928 236.50548 95.43928Q237.1893 95.43928 237.52017 95.16288Q237.85106 94.87543 237.85106 94.49952Q237.85106 94.16784 237.56429 93.968834Q237.35474 93.83617 236.54959 93.63715Q235.45769 93.360756 235.02754 93.16175Q234.60843 92.95168 234.38785 92.59789Q234.17828 92.2441 234.17828 91.81292Q234.17828 91.4149 234.35475 91.08322Q234.53122 90.75155 234.84004 90.530426Q235.07166 90.35353 235.46872 90.24297Q235.8768 90.12135 236.32901 90.12135Q237.02385 90.12135 237.54224 90.32036Q238.06061 90.51937 238.30325 90.86211Q238.55693 91.19379 238.6562 91.76869L237.68561 91.90137Q237.61945 91.448074 237.29959 91.19379Q236.97974 90.9395 236.4062 90.9395Q235.7224 90.9395 235.4246 91.17167Q235.13783 91.39279 235.13783 91.69131Q235.13783 91.89031 235.25916 92.0451Q235.38048 92.199875 235.63416 92.31044Q235.78857 92.365715 236.51651 92.56473Q237.56429 92.841125 237.98341 93.029076Q238.40253 93.20597 238.63414 93.548706Q238.87679 93.89144 238.87679 94.40002Q238.87679 94.89754 238.579 95.339775Q238.29224 95.77096 237.74077 96.01419Q237.20032 96.25742 236.51651 96.25742Q235.36946 96.25742 234.77386 95.78201Q234.17828 95.30661 234.01285 94.36685ZM244.06058 94.23418L245.0863 94.3558Q244.84366 95.26238 244.1819 95.7599Q243.53117 96.25742 242.51646 96.25742Q241.22603 96.25742 240.47604 95.461395Q239.72604 94.66536 239.72604 93.23914Q239.72604 91.75764 240.48706 90.9395Q241.2481 90.12135 242.46132 90.12135Q243.63043 90.12135 244.3694 90.928444Q245.11938 91.72447 245.11938 93.172806Q245.11938 93.26125 245.11938 93.43815L240.75177 93.43815Q240.80692 94.41107 241.2922 94.9307Q241.78853 95.43928 242.51646 95.43928Q243.06793 95.43928 243.45396 95.151825Q243.83998 94.86437 244.06058 94.23418ZM240.80692 92.62L244.07161 92.62Q244.00543 91.87926 243.69661 91.514404Q243.22235 90.9395 242.47235 90.9395Q241.78853 90.9395 241.31427 91.40385Q240.85104 91.85714 240.80692 92.62Z" fill-rule="nonzero"/><path fill="#fce5cd" clip-path="url(#id_0)" d="M255.89592 81.1575L278.2617 81.1575L278.2617 102.79727L255.89592 102.79727Z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="0.7067297530918322" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="0.7067297530918322,2.1201892592754965" clip-path="url(#id_0)" d="M255.89592 81.1575L278.2617 81.1575L278.2617 102.79727L255.89592 102.79727Z" fill-rule="evenodd"/><path fill="#000000" clip-path="url(#id_0)" d="M267.5429 96.873856L267.5429 96.01149Q266.9032 97.02864 265.63483 97.02864Q264.81866 97.02864 264.13483 96.57535Q263.45102 96.122055 263.07602 95.314964Q262.70102 94.50788 262.70102 93.45756Q262.70102 92.42935 263.04294 91.60016Q263.38483 90.7599 264.05762 90.317665Q264.74146 89.87543 265.5907 89.87543Q266.20834 89.87543 266.68262 90.14077Q267.1679 90.39506 267.4657 90.815186L267.4657 87.421L268.62378 87.421L268.62378 96.873856L267.5429 96.873856ZM263.89218 93.45756Q263.89218 94.773224 264.44366 95.42552Q264.99512 96.07783 265.74512 96.07783Q266.50613 96.07783 267.03555 95.458694Q267.56497 94.82851 267.56497 93.55707Q267.56497 92.152954 267.0245 91.500656Q266.48407 90.837296 265.68997 90.837296Q264.92896 90.837296 264.41055 91.46748Q263.89218 92.09768 263.89218 93.45756Z" fill-rule="nonzero"/></svg>
\ No newline at end of file
diff --git a/compose/runtime/design/removing-call-groups-for-non-skippable-functions.md b/compose/runtime/design/removing-call-groups-for-non-skippable-functions.md
new file mode 100644
index 0000000..6eef597
--- /dev/null
+++ b/compose/runtime/design/removing-call-groups-for-non-skippable-functions.md
@@ -0,0 +1,109 @@
+# Removing call groups for non-restartable functions
+
+## Background
+
+Compose defined, as part of the call ABI, that calls must generate a group to ensure the correctness of the application. These groups were informally called call groups. Initially (in early pre-alpha) all calls generated a group. Later this was changed to generate a group in the function itself instead of at the call site. To ensure correctness, control-flow groups were introduced to remove the need for the call site groups.
+
+This document describes a semantics of Compose that preserves correctness without relying on call groups. This was enabled by control-flow groups but the implication of how this affects the need for all calls to have groups was not explored. This document proposes that Compose only keep function groups that are required for restarting.
+
+This document was motivated by Leland Richardson's observation that, although most composable functions are restartable, the majority of groups added to the slot table are for composable functions that are not (e.g. remember, rememberSaveable and all the effects). This is because most composables use these primitives to create higher level concepts and the slot table is dominated by the groups for these primitives.
+
+## Composition as a Call Tree
+
+In [How Compose Works](how-compose-works.md) composition is described in terms of a call tree. A call tree, in this context, is the fully expanded graph of the invocation of where each call has its own node in the tree. Consider, for example, the functions,
+
+```kt
+fun Root() {
+    A(true)
+    A(false)
+}
+
+fun A(value: Boolean) {
+    remember { … }
+    B()
+    B()
+    if (value) C() else D()
+}
+
+fun B() { … remember { … }  … }
+fun C() { … remember { … }  … }
+fun D() { … remember { … }  … }
+```
+
+Calling `Root` will produce a call tree that looks something like,
+
+<image style="background:white;padding:20px;margin:20px" src="./images/call-tree-1.svg" alt="Image of the call tree">
+
+where `B` is repeated because it is called twice and calling `A` twice creates two independent trees. The dotted lines and boxes represent the calls to remember made by each function and represent the slots tracked by the group.
+
+If each function is assigned a unique value to use as a key (Compose uses a compiler generated integer), then the location in the tree can be uniquely identified by the concatenation of these keys if 1) a function is not called twice in the same function and 2) the function is not called in the same relative location conditionally. These two conditions were violated even by the simple code above as we call `A` twice in a row and call `B` twice in a row.
+
+### Call site keys
+To allow for adjacent calls, originally (in pre-alpha) Compose added another key, the call site key, to disambiguate calls like this. Each call site was given another location that, taken together with the function key, disambiguated each group. The combined key of `B`, for example, would distinguish the first call from the second. This requires two keys per call instead of just one.
+
+## Control flow groups
+As described in [How Compose Works](./how-compose-works.md), the call-site key was removed by adding control-flow groups. A control flow group introduces a group around code that contains a branch. That is, it marks when the flow of control might change. This results in the above tree changing to,
+
+<image style="background:white;padding:20px;margin:20px" src="./images/call-tree-2.svg" alt="Image of the call tree">
+
+At first sight this doesn't appear to disambiguate calls to `A` and `B`. It is ensured, however, by the compiler ensuring that, absent a control flow group, the call will occur unconditionally. That is `A` will always be called twice in the order given and `B` will always, unconditionally, be called twice. If it could be called conditionally then it would have a control flow group. This means that the call is disambiguated by considering the order they are called. If the keys are combined with the index in which they are called in the parent group, the concatenated key is guaranteed to be unique for the location in the tree.
+
+If `A` and `B` are always unconditionally executed, are the groups needed at all?
+
+A group is needed, as described in [How Compose Works](how-compose-works.md), to ensure that positional memoization has the expected semantics. If `A` calls `remember` then each `A` in the graph above must return a unique value when it is recomposed but the same value for the same location in the graph for the lifetime of the composition. This is done by associating the slots needed by calls to `remember` to uniquely identifiable groups in the tree as described above. For each invocation of a function, the composer can find the previous state keys generated by the compiler to find the corresponding slots in the slot table.
+
+However, the function groups are not needed for this, just unique addressing in the tree.
+
+To see this, let's reorganize the tree along the lines of flow of control, instead of calls.
+
+If the state of the above tree is laid out linearly it would look like,
+
+<image style="background:white;padding:20px;margin:20px" src="./images/call-tree-4.svg" alt="Image of the call tree">
+
+As the calls to A and B are unconditional (otherwise they would have a flow-control group around them), the slots for `A` and `B` will always be adjacent to each other in a fixed order.
+
+The only variation is the state for the If blocks that contain C and D. If the tree was re-organized to be,
+
+<image style="background:white;padding:20px;margin:20px" src="./images/call-tree-3.svg" alt="Image of the call tree">
+
+which reorganizes the tree by when the slots will always be in the same order and only creates a separate node for when the order or number of slots can change (assuming, for example, that `C` and `D` could have a different number of slots) the nodes of this tree are equally uniquely addressable given the key+order assumptions introduced above.
+
+Since the order of the calls is fixed, the call groups are no longer necessary to address the tree, only the control flow groups are necessary so the tree can be written as,
+
+<image style="background:white;padding:20px;margin:20px" src="./images/call-tree-6.svg" alt="Image of the call tree">
+
+This tree requires the same number of slots, but has significantly fewer groups.
+
+### Restarting and skipping
+Composable functions do not need function groups to uniquely identify their state. However, they do need a group to be skipped and restarted. A group is used to mark the region of the slot table that contains the state of a restartable function and the region of the slot table that can be skipped if the function is skipped. This means that not all function groups can be removed, only groups for non-restartable functions. As noted above, however, even though this is a minority of the total of composable functions written, the number of non-restartable functions called is quite high as functions like remember are called by nearly every restartable function.
+
+## Impact on other systems
+
+### Slot Table
+
+The current slot table implementation requires that once a child group is created, no more slots can be added to the group. This means that any function that adds slots to the slot table requires a group around it as it can be called after a group has already been added by another function called before it.
+
+If we lift the restriction that would allow slots to be added after child groups then `remember` would not need its own group as `remember` it could just add slots to the caller's group.
+
+To allow removing groups around non-skipable functions, the slot table needs to be changed to allow adding slots to a group that already has child groups.
+
+Adding this to the existing slot table impacts both the `SlotReader` and `SlotWriter`. The `SlotReader` will now need to track where the parent left off reading the slots when a child group is entered and restore that when the child group is closed. This is not necessary today as the slots are never read after a child is entered. The `SlotWriter` would need to handle the case where slots are added after a child group is entered which it currently ignores. Both of these changes are relatively simple but the `SlotReader` might slow down recomposition as it must now do more work.
+
+### Read-only composable
+Marking a composable as read-only allowed the compiler to remove the group as the composable was guaranteed to not add a group. With this change if the function is not restartable and doesn't call remember it will generate the same code as read only composable meaning that read-only composable is effectively an alias for non-restartable.
+
+### Tooling
+
+#### Source information
+Source information used by the inspector is stored in the group for the function. This took advantage of the fact that all functions introduced groups. To support read-only functions, a start/end source marker was added that would conditionally add a group if source information is collected. This can be reused for non-restartable functions.
+
+The source information is currently needed to find the call that generated a layout node. As Box, Column, and Row are such calls as they are non-restarable (as they are inline functions) source information is required for these functions. This implies that it is inferred when a function generates a layout node, which is hard, or mark such functions with an annotation or always generates source markers for non-restartable functions, which are both simple.
+
+#### Slot tree
+The layout inspector and animation inspector both rely on the slot tree which, by naming convention, exposes the call tree based model of navigation. The naming convention might need to change here to reflect that a group doesn't necessarily map to a call which was true already as we already had control flow groups that are not call groups.
+
+#### Tracing
+Tracing is independent of the start and end groups and are unaffected by this proposal.
+
+## Composite Key Hash
+Composite keys are used to uniquely identify state information that is preserved across restarting the activity or process. As these keys are a proxy for concatenating the keys as described above (they are hashed together instead of concatenated) they will still be as unique as they were previously.
diff --git a/compose/runtime/runtime/build.gradle b/compose/runtime/runtime/build.gradle
index 6cac6fe..638c246 100644
--- a/compose/runtime/runtime/build.gradle
+++ b/compose/runtime/runtime/build.gradle
@@ -41,7 +41,7 @@
             dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 implementation(libs.kotlinCoroutinesCore)
-                implementation(project(":collection:collection"))
+                implementation("androidx.collection:collection:1.4.0")
             }
         }
 
@@ -82,7 +82,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api(libs.kotlinCoroutinesAndroid)
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
             }
         }
 
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt
index 2e929a6..03c5ce9 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFlow.kt
@@ -18,16 +18,15 @@
 @file:JvmMultifileClass
 package androidx.compose.runtime
 
-import androidx.compose.runtime.collection.IdentityArraySet
+import androidx.collection.MutableScatterSet
+import androidx.compose.runtime.snapshots.ReaderKind
 import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.runtime.snapshots.StateObjectImpl
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.jvm.JvmMultifileClass
-import kotlin.jvm.JvmName
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.withContext
 
@@ -111,8 +110,13 @@
     block: () -> T
 ): Flow<T> = flow {
     // Objects read the last time block was run
-    val readSet = IdentityArraySet<Any>()
-    val readObserver: (Any) -> Unit = { readSet.add(it) }
+    val readSet = MutableScatterSet<Any>()
+    val readObserver: (Any) -> Unit = {
+        if (it is StateObjectImpl) {
+            it.recordReadIn(ReaderKind.SnapshotFlow)
+        }
+        readSet.add(it)
+    }
 
     // This channel may not block or lose data on a trySend call.
     val appliedChanges = Channel<Set<Any>>(Channel.UNLIMITED)
@@ -120,7 +124,13 @@
     // Register the apply observer before running for the first time
     // so that we don't miss updates.
     val unregisterApplyObserver = Snapshot.registerApplyObserver { changed, _ ->
-        appliedChanges.trySend(changed)
+        val maybeObserved = changed.any {
+            it !is StateObjectImpl || it.isReadIn(ReaderKind.SnapshotFlow)
+        }
+
+        if (maybeObserved) {
+            appliedChanges.trySend(changed)
+        }
     }
 
     try {
@@ -140,7 +150,7 @@
             // Poll for any other changes before running block to minimize the number of
             // additional times it runs for the same data
             while (true) {
-                // Assumption: readSet will typically be smaller than changed
+                // Assumption: readSet will typically be smaller than changed set
                 found = found || readSet.intersects(changedObjects)
                 changedObjects = appliedChanges.tryReceive().getOrNull() ?: break
             }
@@ -166,12 +176,5 @@
     }
 }
 
-/**
- * Return `true` if there are any elements shared between `this` and [other]
- */
-private fun <T : Any> IdentityArraySet<T>.intersects(other: Set<T>): Boolean =
-    when {
-        size < other.size -> fastAny { it in other }
-        other is IdentityArraySet<T> -> other.fastAny { it in this }
-        else -> other.any { it in this }
-    }
+private fun MutableScatterSet<Any>.intersects(set: Set<Any>) =
+    any { it in set }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/StateObjectImpl.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/StateObjectImpl.kt
index de2f9b8b..497e770 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/StateObjectImpl.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/StateObjectImpl.kt
@@ -49,5 +49,6 @@
     internal companion object {
         inline val Composition get() = ReaderKind(mask = 1 shl 0)
         inline val SnapshotStateObserver get() = ReaderKind(mask = 1 shl 1)
+        inline val SnapshotFlow get() = ReaderKind(mask = 1 shl 2)
     }
 }
diff --git a/compose/ui/ui-graphics/build.gradle b/compose/ui/ui-graphics/build.gradle
index d44a291..3f9cc28 100644
--- a/compose/ui/ui-graphics/build.gradle
+++ b/compose/ui/ui-graphics/build.gradle
@@ -45,7 +45,7 @@
                 api(project(":compose:ui:ui-unit"))
                 implementation(project(":compose:runtime:runtime"))
                 implementation(project(":compose:ui:ui-util"))
-                implementation(project(":collection:collection"))
+                implementation("androidx.collection:collection:1.4.0")
             }
         }
 
@@ -72,11 +72,9 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                //TODO: Switch to pinned version when beta1 is released as it fixes bugs
-                //implementation("androidx.graphics:graphics-path:1.0.0-beta01")
-                implementation(project(":graphics:graphics-path"))
+                implementation("androidx.graphics:graphics-path:1.0.0-beta02")
                 api(libs.androidx.annotation)
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
             }
         }
 
diff --git a/compose/ui/ui-text/api/current.ignore b/compose/ui/ui-text/api/current.ignore
index 8e2ec26..6aede09 100644
--- a/compose/ui/ui-text/api/current.ignore
+++ b/compose/ui/ui-text/api/current.ignore
@@ -3,5 +3,25 @@
     Class androidx.compose.ui.text.input.PlatformImeOptions changed class/interface declaration
 
 
+ChangedType: androidx.compose.ui.text.ParagraphStyle#getHyphens():
+    Method androidx.compose.ui.text.ParagraphStyle.getHyphens has changed return type from androidx.compose.ui.text.style.Hyphens to int
+ChangedType: androidx.compose.ui.text.ParagraphStyle#getLineBreak():
+    Method androidx.compose.ui.text.ParagraphStyle.getLineBreak has changed return type from androidx.compose.ui.text.style.LineBreak to int
+ChangedType: androidx.compose.ui.text.ParagraphStyle#getTextAlign():
+    Method androidx.compose.ui.text.ParagraphStyle.getTextAlign has changed return type from androidx.compose.ui.text.style.TextAlign to int
+ChangedType: androidx.compose.ui.text.ParagraphStyle#getTextDirection():
+    Method androidx.compose.ui.text.ParagraphStyle.getTextDirection has changed return type from androidx.compose.ui.text.style.TextDirection to int
+
+
+InvalidNullConversion: androidx.compose.ui.text.ParagraphStyle#getHyphens():
+    Attempted to remove @Nullable annotation from method androidx.compose.ui.text.ParagraphStyle.getHyphens()
+InvalidNullConversion: androidx.compose.ui.text.ParagraphStyle#getLineBreak():
+    Attempted to remove @Nullable annotation from method androidx.compose.ui.text.ParagraphStyle.getLineBreak()
+InvalidNullConversion: androidx.compose.ui.text.ParagraphStyle#getTextAlign():
+    Attempted to remove @Nullable annotation from method androidx.compose.ui.text.ParagraphStyle.getTextAlign()
+InvalidNullConversion: androidx.compose.ui.text.ParagraphStyle#getTextDirection():
+    Attempted to remove @Nullable annotation from method androidx.compose.ui.text.ParagraphStyle.getTextDirection()
+
+
 RemovedClass: androidx.compose.ui.text.input.AndroidImeOptions:
     Removed class androidx.compose.ui.text.input.AndroidImeOptions
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index 921509d..7196bff 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -278,17 +278,17 @@
     method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
     method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     method public androidx.compose.ui.text.ParagraphStyle copy(optional int textAlign, optional int textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional int lineBreak, optional int hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
-    method @Deprecated public androidx.compose.ui.text.style.Hyphens? getHyphens();
     method public int getHyphens();
-    method @Deprecated public androidx.compose.ui.text.style.LineBreak? getLineBreak();
+    method @Deprecated public androidx.compose.ui.text.style.Hyphens? getHyphens-EaSxIns();
     method public int getLineBreak();
+    method @Deprecated public androidx.compose.ui.text.style.LineBreak? getLineBreak-LgCVezo();
     method public long getLineHeight();
     method public androidx.compose.ui.text.style.LineHeightStyle? getLineHeightStyle();
     method public androidx.compose.ui.text.PlatformParagraphStyle? getPlatformStyle();
-    method @Deprecated public androidx.compose.ui.text.style.TextAlign? getTextAlign();
     method public int getTextAlign();
-    method @Deprecated public androidx.compose.ui.text.style.TextDirection? getTextDirection();
+    method @Deprecated public androidx.compose.ui.text.style.TextAlign? getTextAlign-buA522U();
     method public int getTextDirection();
+    method @Deprecated public androidx.compose.ui.text.style.TextDirection? getTextDirection-mmuk1to();
     method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
     method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.ParagraphStyle merge(optional androidx.compose.ui.text.ParagraphStyle? other);
@@ -595,20 +595,20 @@
     method public androidx.compose.ui.text.font.FontSynthesis? getFontSynthesis();
     method public androidx.compose.ui.text.font.FontWeight? getFontWeight();
     method public int getHyphens();
-    method @Deprecated public androidx.compose.ui.text.style.Hyphens? getHyphens();
+    method @Deprecated public androidx.compose.ui.text.style.Hyphens? getHyphens-EaSxIns();
     method public long getLetterSpacing();
     method public int getLineBreak();
-    method @Deprecated public androidx.compose.ui.text.style.LineBreak? getLineBreak();
+    method @Deprecated public androidx.compose.ui.text.style.LineBreak? getLineBreak-LgCVezo();
     method public long getLineHeight();
     method public androidx.compose.ui.text.style.LineHeightStyle? getLineHeightStyle();
     method public androidx.compose.ui.text.intl.LocaleList? getLocaleList();
     method public androidx.compose.ui.text.PlatformTextStyle? getPlatformStyle();
     method public androidx.compose.ui.graphics.Shadow? getShadow();
     method public int getTextAlign();
-    method @Deprecated public androidx.compose.ui.text.style.TextAlign? getTextAlign();
+    method @Deprecated public androidx.compose.ui.text.style.TextAlign? getTextAlign-buA522U();
     method public androidx.compose.ui.text.style.TextDecoration? getTextDecoration();
     method public int getTextDirection();
-    method @Deprecated public androidx.compose.ui.text.style.TextDirection? getTextDirection();
+    method @Deprecated public androidx.compose.ui.text.style.TextDirection? getTextDirection-mmuk1to();
     method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
     method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
     method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
diff --git a/compose/ui/ui-text/api/restricted_current.ignore b/compose/ui/ui-text/api/restricted_current.ignore
index 8e2ec26..6aede09 100644
--- a/compose/ui/ui-text/api/restricted_current.ignore
+++ b/compose/ui/ui-text/api/restricted_current.ignore
@@ -3,5 +3,25 @@
     Class androidx.compose.ui.text.input.PlatformImeOptions changed class/interface declaration
 
 
+ChangedType: androidx.compose.ui.text.ParagraphStyle#getHyphens():
+    Method androidx.compose.ui.text.ParagraphStyle.getHyphens has changed return type from androidx.compose.ui.text.style.Hyphens to int
+ChangedType: androidx.compose.ui.text.ParagraphStyle#getLineBreak():
+    Method androidx.compose.ui.text.ParagraphStyle.getLineBreak has changed return type from androidx.compose.ui.text.style.LineBreak to int
+ChangedType: androidx.compose.ui.text.ParagraphStyle#getTextAlign():
+    Method androidx.compose.ui.text.ParagraphStyle.getTextAlign has changed return type from androidx.compose.ui.text.style.TextAlign to int
+ChangedType: androidx.compose.ui.text.ParagraphStyle#getTextDirection():
+    Method androidx.compose.ui.text.ParagraphStyle.getTextDirection has changed return type from androidx.compose.ui.text.style.TextDirection to int
+
+
+InvalidNullConversion: androidx.compose.ui.text.ParagraphStyle#getHyphens():
+    Attempted to remove @Nullable annotation from method androidx.compose.ui.text.ParagraphStyle.getHyphens()
+InvalidNullConversion: androidx.compose.ui.text.ParagraphStyle#getLineBreak():
+    Attempted to remove @Nullable annotation from method androidx.compose.ui.text.ParagraphStyle.getLineBreak()
+InvalidNullConversion: androidx.compose.ui.text.ParagraphStyle#getTextAlign():
+    Attempted to remove @Nullable annotation from method androidx.compose.ui.text.ParagraphStyle.getTextAlign()
+InvalidNullConversion: androidx.compose.ui.text.ParagraphStyle#getTextDirection():
+    Attempted to remove @Nullable annotation from method androidx.compose.ui.text.ParagraphStyle.getTextDirection()
+
+
 RemovedClass: androidx.compose.ui.text.input.AndroidImeOptions:
     Removed class androidx.compose.ui.text.input.AndroidImeOptions
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index 066a144..9e9bcf6 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -278,17 +278,17 @@
     method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
     method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
     method public androidx.compose.ui.text.ParagraphStyle copy(optional int textAlign, optional int textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional int lineBreak, optional int hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
-    method @Deprecated public androidx.compose.ui.text.style.Hyphens? getHyphens();
     method public int getHyphens();
-    method @Deprecated public androidx.compose.ui.text.style.LineBreak? getLineBreak();
+    method @Deprecated public androidx.compose.ui.text.style.Hyphens? getHyphens-EaSxIns();
     method public int getLineBreak();
+    method @Deprecated public androidx.compose.ui.text.style.LineBreak? getLineBreak-LgCVezo();
     method public long getLineHeight();
     method public androidx.compose.ui.text.style.LineHeightStyle? getLineHeightStyle();
     method public androidx.compose.ui.text.PlatformParagraphStyle? getPlatformStyle();
-    method @Deprecated public androidx.compose.ui.text.style.TextAlign? getTextAlign();
     method public int getTextAlign();
-    method @Deprecated public androidx.compose.ui.text.style.TextDirection? getTextDirection();
+    method @Deprecated public androidx.compose.ui.text.style.TextAlign? getTextAlign-buA522U();
     method public int getTextDirection();
+    method @Deprecated public androidx.compose.ui.text.style.TextDirection? getTextDirection-mmuk1to();
     method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
     method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
     method @androidx.compose.runtime.Stable public androidx.compose.ui.text.ParagraphStyle merge(optional androidx.compose.ui.text.ParagraphStyle? other);
@@ -595,20 +595,20 @@
     method public androidx.compose.ui.text.font.FontSynthesis? getFontSynthesis();
     method public androidx.compose.ui.text.font.FontWeight? getFontWeight();
     method public int getHyphens();
-    method @Deprecated public androidx.compose.ui.text.style.Hyphens? getHyphens();
+    method @Deprecated public androidx.compose.ui.text.style.Hyphens? getHyphens-EaSxIns();
     method public long getLetterSpacing();
     method public int getLineBreak();
-    method @Deprecated public androidx.compose.ui.text.style.LineBreak? getLineBreak();
+    method @Deprecated public androidx.compose.ui.text.style.LineBreak? getLineBreak-LgCVezo();
     method public long getLineHeight();
     method public androidx.compose.ui.text.style.LineHeightStyle? getLineHeightStyle();
     method public androidx.compose.ui.text.intl.LocaleList? getLocaleList();
     method public androidx.compose.ui.text.PlatformTextStyle? getPlatformStyle();
     method public androidx.compose.ui.graphics.Shadow? getShadow();
     method public int getTextAlign();
-    method @Deprecated public androidx.compose.ui.text.style.TextAlign? getTextAlign();
+    method @Deprecated public androidx.compose.ui.text.style.TextAlign? getTextAlign-buA522U();
     method public androidx.compose.ui.text.style.TextDecoration? getTextDecoration();
     method public int getTextDirection();
-    method @Deprecated public androidx.compose.ui.text.style.TextDirection? getTextDirection();
+    method @Deprecated public androidx.compose.ui.text.style.TextDirection? getTextDirection-mmuk1to();
     method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
     method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
     method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
diff --git a/compose/ui/ui-text/build.gradle b/compose/ui/ui-text/build.gradle
index 183c508..dc89b2a 100644
--- a/compose/ui/ui-text/build.gradle
+++ b/compose/ui/ui-text/build.gradle
@@ -81,7 +81,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api("androidx.annotation:annotation:1.1.0")
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
                 implementation("androidx.core:core:1.7.0")
                 implementation("androidx.emoji2:emoji2:1.2.0")
                 implementation('androidx.collection:collection:1.0.0')
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt
index 10058d2..362c2cd 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt
@@ -540,8 +540,8 @@
          * When clicking on the text in [range], the corresponding URL from the [url] annotation
          * will be opened using [androidx.compose.ui.platform.UriHandler].
          *
-         * URLs may be treated specially by screen readers, including being identified while reading text with an audio icon or being
-         * summarized in a links menu. When the text
+         * URLs may be treated specially by screen readers, including being identified while
+         * reading text with an audio icon or being summarized in a links menu.
          *
          * @param url A [LinkAnnotation.Url] object that stores the URL being linked to.
          * @param start the inclusive starting offset of the range
@@ -556,9 +556,13 @@
         /**
          * Set a [LinkAnnotation.Clickable] for the given [range].
          *
-         * When clicking on the text in [range], the handler will be triggered with the tag
+         * When clicking on the text in [range], the
+         * [androidx.compose.foundation.TextLinkClickHandler] will be triggered with the tag
          * corresponding to the [clickable] object.
          *
+         * Clickable link may be treated specially by screen readers, including being identified
+         * while reading text with an audio icon or being summarized in a links menu.
+         *
          * @param clickable A [LinkAnnotation.Clickable] object that stores the tag being linked to.
          * @param start the inclusive starting offset of the range
          * @param end the exclusive end offset of the range
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkAnnotation.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkAnnotation.kt
index c46ab75..cc962f9 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkAnnotation.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkAnnotation.kt
@@ -45,10 +45,8 @@
 
     /**
      * An annotation that contains a clickable marked with [tag]. When clicking on the text to
-     * which this annotation is attached, the app will trigger a handler's callback.
-     *
-     * Disclaimer: This is a no-op at the moment. Continue using
-     * [androidx.compose.foundation.text.ClickableText] to make your text clickable.
+     * which this annotation is attached, the app will trigger a
+     * [androidx.compose.foundation.TextLinkClickHandler.onClick] callback.
      */
     class Clickable(val tag: String) : LinkAnnotation() {
         override fun equals(other: Any?): Boolean {
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
index 4137c8c..c6005aa 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
@@ -75,23 +75,23 @@
     val textMotion: TextMotion? = null
 ) {
     @Deprecated("Kept for backwards compatibility.", level = DeprecationLevel.WARNING)
-    @get:JvmName("getTextAlign")
-    @Suppress("unused")
+    @get:JvmName("getTextAlign-buA522U") // b/320819734
+    @Suppress("unused", "RedundantNullableReturnType", "PropertyName")
     val deprecated_boxing_textAlign: TextAlign? get() = this.textAlign
 
     @Deprecated("Kept for backwards compatibility.", level = DeprecationLevel.WARNING)
-    @get:JvmName("getTextDirection")
-    @Suppress("unused")
+    @get:JvmName("getTextDirection-mmuk1to") // b/320819734
+    @Suppress("unused", "RedundantNullableReturnType", "PropertyName")
     val deprecated_boxing_textDirection: TextDirection? get() = this.textDirection
 
     @Deprecated("Kept for backwards compatibility.", level = DeprecationLevel.WARNING)
-    @get:JvmName("getHyphens")
-    @Suppress("unused")
+    @get:JvmName("getHyphens-EaSxIns") // b/320819734
+    @Suppress("unused", "RedundantNullableReturnType", "PropertyName")
     val deprecated_boxing_hyphens: Hyphens? get() = this.hyphens
 
     @Deprecated("Kept for backwards compatibility.", level = DeprecationLevel.WARNING)
-    @get:JvmName("getLineBreak")
-    @Suppress("unused")
+    @get:JvmName("getLineBreak-LgCVezo") // b/320819734
+    @Suppress("unused", "RedundantNullableReturnType", "PropertyName")
     val deprecated_boxing_lineBreak: LineBreak? get() = this.lineBreak
 
     @Deprecated("ParagraphStyle constructors that take nullable TextAlign, " +
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
index 061b743..e3fad6c 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
@@ -1405,8 +1405,8 @@
     val textAlign: TextAlign get() = this.paragraphStyle.textAlign
 
     @Deprecated("Kept for backwards compatibility.", level = DeprecationLevel.WARNING)
-    @get:JvmName("getTextAlign")
-    @Suppress("unused")
+    @get:JvmName("getTextAlign-buA522U") // b/320819734
+    @Suppress("unused", "RedundantNullableReturnType", "PropertyName")
     val deprecated_boxing_textAlign: TextAlign? get() = this.textAlign
 
     /**
@@ -1417,8 +1417,8 @@
     val textDirection: TextDirection get() = this.paragraphStyle.textDirection
 
     @Deprecated("Kept for backwards compatibility.", level = DeprecationLevel.WARNING)
-    @get:JvmName("getTextDirection")
-    @Suppress("unused")
+    @get:JvmName("getTextDirection-mmuk1to") // b/320819734
+    @Suppress("unused", "RedundantNullableReturnType", "PropertyName")
     val deprecated_boxing_textDirection: TextDirection? get() = this.textDirection
 
     /**
@@ -1447,8 +1447,8 @@
     val hyphens: Hyphens get() = this.paragraphStyle.hyphens
 
     @Deprecated("Kept for backwards compatibility.", level = DeprecationLevel.WARNING)
-    @get:JvmName("getHyphens")
-    @Suppress("unused")
+    @get:JvmName("getHyphens-EaSxIns") // b/320819734
+    @Suppress("unused", "RedundantNullableReturnType", "PropertyName")
     val deprecated_boxing_hyphens: Hyphens? get() = this.hyphens
 
     /**
@@ -1457,8 +1457,8 @@
     val lineBreak: LineBreak get() = this.paragraphStyle.lineBreak
 
     @Deprecated("Kept for backwards compatibility.", level = DeprecationLevel.WARNING)
-    @get:JvmName("getLineBreak")
-    @Suppress("unused")
+    @get:JvmName("getLineBreak-LgCVezo") // b/320819734
+    @Suppress("unused", "RedundantNullableReturnType", "PropertyName")
     val deprecated_boxing_lineBreak: LineBreak? get() = this.lineBreak
 
     /**
diff --git a/compose/ui/ui-tooling-data/build.gradle b/compose/ui/ui-tooling-data/build.gradle
index bfae122..1f20915 100644
--- a/compose/ui/ui-tooling-data/build.gradle
+++ b/compose/ui/ui-tooling-data/build.gradle
@@ -70,7 +70,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api("androidx.annotation:annotation:1.1.0")
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
             }
         }
 
diff --git a/compose/ui/ui-unit/build.gradle b/compose/ui/ui-unit/build.gradle
index 40dc10b..f10e874 100644
--- a/compose/ui/ui-unit/build.gradle
+++ b/compose/ui/ui-unit/build.gradle
@@ -44,7 +44,7 @@
 
                 implementation(project(":compose:runtime:runtime"))
                 implementation(project(":compose:ui:ui-util"))
-                implementation(project(":collection:collection"))
+                implementation("androidx.collection:collection:1.4.0")
                 api("androidx.annotation:annotation:1.1.0")
             }
         }
@@ -67,7 +67,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api("androidx.annotation:annotation:1.1.0")
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
                 implementation('androidx.collection:collection-ktx:1.2.0')
             }
         }
diff --git a/compose/ui/ui-util/build.gradle b/compose/ui/ui-util/build.gradle
index 97f170c7..b71de8d 100644
--- a/compose/ui/ui-util/build.gradle
+++ b/compose/ui/ui-util/build.gradle
@@ -61,7 +61,7 @@
             dependsOn(jvmMain)
             dependencies {
                 implementation(libs.kotlinStdlib)
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
             }
         }
 
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 8bc9584..3a4da7e 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -2821,7 +2821,7 @@
     method public androidx.compose.ui.text.AnnotatedString? getText();
     method public default boolean hasClip();
     method public default boolean hasText();
-    method public default void setClip(androidx.compose.ui.platform.ClipEntry clipEntry, optional androidx.compose.ui.platform.ClipMetadata? clipMetadata);
+    method public default void setClip(androidx.compose.ui.platform.ClipEntry clipEntry);
     method public void setText(androidx.compose.ui.text.AnnotatedString annotatedString);
     property public default android.content.ClipboardManager nativeClipboard;
   }
@@ -3606,8 +3606,9 @@
   }
 
   @androidx.compose.runtime.Immutable public final class DialogProperties {
-    ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy);
+    ctor @Deprecated public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy);
     ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean usePlatformDefaultWidth, optional boolean decorFitsSystemWindows);
+    ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional boolean usePlatformDefaultWidth);
     method public boolean getDecorFitsSystemWindows();
     method public boolean getDismissOnBackPress();
     method public boolean getDismissOnClickOutside();
@@ -3632,6 +3633,7 @@
   @androidx.compose.runtime.Immutable public final class PopupProperties {
     ctor public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean excludeFromSystemGesture, optional boolean clippingEnabled);
     ctor @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean excludeFromSystemGesture, optional boolean clippingEnabled, optional boolean usePlatformDefaultWidth);
+    ctor public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional boolean clippingEnabled);
     method public boolean getClippingEnabled();
     method public boolean getDismissOnBackPress();
     method public boolean getDismissOnClickOutside();
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 682b802..3dd5a13 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -2874,7 +2874,7 @@
     method public androidx.compose.ui.text.AnnotatedString? getText();
     method public default boolean hasClip();
     method public default boolean hasText();
-    method public default void setClip(androidx.compose.ui.platform.ClipEntry clipEntry, optional androidx.compose.ui.platform.ClipMetadata? clipMetadata);
+    method public default void setClip(androidx.compose.ui.platform.ClipEntry clipEntry);
     method public void setText(androidx.compose.ui.text.AnnotatedString annotatedString);
     property public default android.content.ClipboardManager nativeClipboard;
   }
@@ -3666,8 +3666,9 @@
   }
 
   @androidx.compose.runtime.Immutable public final class DialogProperties {
-    ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy);
+    ctor @Deprecated public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy);
     ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean usePlatformDefaultWidth, optional boolean decorFitsSystemWindows);
+    ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional boolean usePlatformDefaultWidth);
     method public boolean getDecorFitsSystemWindows();
     method public boolean getDismissOnBackPress();
     method public boolean getDismissOnClickOutside();
@@ -3692,6 +3693,7 @@
   @androidx.compose.runtime.Immutable public final class PopupProperties {
     ctor public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean excludeFromSystemGesture, optional boolean clippingEnabled);
     ctor @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean excludeFromSystemGesture, optional boolean clippingEnabled, optional boolean usePlatformDefaultWidth);
+    ctor public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional boolean clippingEnabled);
     method public boolean getClippingEnabled();
     method public boolean getDismissOnBackPress();
     method public boolean getDismissOnClickOutside();
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index 2ea3cd1..69ae2b2 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -44,7 +44,7 @@
                 implementation(libs.kotlinStdlibCommon)
                 implementation(libs.kotlinCoroutinesCore)
                 api("androidx.annotation:annotation:1.6.0")
-                implementation(project(":collection:collection"))
+                implementation("androidx.collection:collection:1.4.0")
                 // when updating the runtime version please also update the runtime-saveable version
                 implementation(project(":compose:runtime:runtime"))
                 api(project(":compose:runtime:runtime-saveable"))
@@ -82,7 +82,7 @@
             dependsOn(jvmMain)
             dependencies {
                 implementation(libs.kotlinStdlib)
-                api(project(":annotation:annotation-experimental"))
+                api("androidx.annotation:annotation-experimental:1.4.0")
                 // This has stub APIs for access to legacy Android APIs, so we don't want
                 // any dependency on this module.
                 compileOnly(project(":compose:ui:ui-android-stubs"))
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
index 407c8a6..d53a555 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
@@ -1200,7 +1200,7 @@
         var box2LayoutCoordinates: LayoutCoordinates? = null
         var box3LayoutCoordinates: LayoutCoordinates? = null
 
-        val setUpFinishedLatch = CountDownLatch(1)
+        val setUpFinishedLatch = CountDownLatch(4)
         // One less than total because outside is not sent to Compose.
         val totalEventLatch = CountDownLatch(5)
 
@@ -1242,6 +1242,7 @@
                             .size(50.dp)
                             .onGloballyPositioned {
                                 box1LayoutCoordinates = it
+                                setUpFinishedLatch.countDown()
                             }
                             .pointerInput(Unit) {
                                 awaitPointerEventScope {
@@ -1270,6 +1271,7 @@
                             .size(50.dp)
                             .onGloballyPositioned {
                                 box2LayoutCoordinates = it
+                                setUpFinishedLatch.countDown()
                             }
                             .pointerInput(Unit) {
                                 awaitPointerEventScope {
@@ -1304,6 +1306,7 @@
                             .size(50.dp)
                             .onGloballyPositioned {
                                 box3LayoutCoordinates = it
+                                setUpFinishedLatch.countDown()
                             }
                             .pointerInput(Unit) {
                                 awaitPointerEventScope {
@@ -1318,7 +1321,7 @@
             }
         }
         // Ensure Arrange (setup) step is finished
-        assertTrue(setUpFinishedLatch.await(1, TimeUnit.SECONDS))
+        assertTrue(setUpFinishedLatch.await(2, TimeUnit.SECONDS))
 
         // --> Act + Assert (interwoven)
         // Hover Enter on Box 1
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/platform/AndroidClipboardManagerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/platform/AndroidClipboardManagerTest.kt
index f0cd9f6..df0dbf1 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/platform/AndroidClipboardManagerTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/platform/AndroidClipboardManagerTest.kt
@@ -288,7 +288,7 @@
         val clipData = mock<ClipData>()
         val subject = AndroidClipboardManager(clipboardManager)
 
-        subject.setClip(clipData.toClipEntry(), null)
+        subject.setClip(clipData.toClipEntry())
 
         verify(clipboardManager, times(1)).setPrimaryClip(clipData)
     }
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/PopupLayoutTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/PopupLayoutTest.kt
index dd2fd50..08e9912 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/PopupLayoutTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/PopupLayoutTest.kt
@@ -275,6 +275,31 @@
         }
     }
 
+    @Test
+    fun positionNotUpdated_whenDetached() {
+        val coordinates = MutableLayoutCoordinates(isAttached = false)
+        val layout = createPopupLayout(
+            positionProvider = object : PopupPositionProvider {
+                override fun calculatePosition(
+                    anchorBounds: IntRect,
+                    windowSize: IntSize,
+                    layoutDirection: LayoutDirection,
+                    popupContentSize: IntSize
+                ): IntOffset = anchorBounds.topLeft
+            },
+        )
+        layout.popupContentSize = IntSize.Zero
+
+        assertThat(layout.params.x).isEqualTo(0)
+        assertThat(layout.params.y).isEqualTo(0)
+
+        coordinates.windowOffset = Offset(50f, 50f)
+        layout.updateParentLayoutCoordinates(coordinates)
+
+        assertThat(layout.params.x).isEqualTo(0)
+        assertThat(layout.params.y).isEqualTo(0)
+    }
+
     private fun createPopupLayout(
         onDismissRequest: (() -> Unit)? = null,
         properties: PopupProperties = PopupProperties(),
@@ -329,12 +354,13 @@
          * An implementation of [LayoutCoordinates] that allows explicitly setting values but only
          * supports the minimum required subset of operations that [PopupLayout] uses.
          */
-        private class MutableLayoutCoordinates : LayoutCoordinates {
+        private class MutableLayoutCoordinates(
+            override var isAttached: Boolean = true
+        ) : LayoutCoordinates {
             override var size: IntSize = IntSize.Zero
             override val providedAlignmentLines: Set<AlignmentLine> = emptySet()
             override var parentLayoutCoordinates: LayoutCoordinates? = null
             override var parentCoordinates: LayoutCoordinates? = null
-            override var isAttached: Boolean = false
 
             var windowOffset: Offset = Offset.Zero
 
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/contentcapture/AndroidContentCaptureManager.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/contentcapture/AndroidContentCaptureManager.android.kt
index d6aef9b..a327d8b 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/contentcapture/AndroidContentCaptureManager.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/contentcapture/AndroidContentCaptureManager.android.kt
@@ -32,6 +32,7 @@
 import androidx.collection.MutableIntObjectMap
 import androidx.collection.intObjectMapOf
 import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.platform.AndroidComposeView
 import androidx.compose.ui.platform.ScrollObservationScope
@@ -251,7 +252,7 @@
 
         newNode.replacedChildren.fastForEach { child ->
             if (currentSemanticsNodes.contains(child.id)) {
-                val prevNode = checkNotNull(previousSemanticsNodes[child.id]) {
+                val prevNode = checkPreconditionNotNull(previousSemanticsNodes[child.id]) {
                     "node not present in pruned tree before this change"
                 }
                 sendSemanticsStructureChangeEvents(child, prevNode)
@@ -280,7 +281,7 @@
         newNode.replacedChildren.fastForEach { child ->
             if (currentSemanticsNodes.contains(child.id) &&
                 previousSemanticsNodes.contains(child.id)) {
-                val prevNodeCopy = checkNotNull(previousSemanticsNodes[child.id]) {
+                val prevNodeCopy = checkPreconditionNotNull(previousSemanticsNodes[child.id]) {
                     "node not present in pruned tree before this change"
                 }
                 sendContentCaptureStructureChangeEvents(child, prevNodeCopy)
@@ -301,7 +302,7 @@
             // We do doing this search because the new configuration is set as a whole, so we
             // can't indicate which property is changed when setting the new configuration.
             val oldNode = previousSemanticsNodes[id]
-            val newNode = checkNotNull(newSemanticsNodes[id]?.semanticsNode) {
+            val newNode = checkPreconditionNotNull(newSemanticsNodes[id]?.semanticsNode) {
                 "no value for specified key"
             }
 
@@ -329,7 +330,7 @@
                     }
                     SemanticsProperties.VerticalScrollAxisRange -> {
                         notifySubtreeStateChangeIfNeeded(newNode.layoutNode)
-                        val scope = checkNotNull(scrollObservationScopes.findById(id)) {
+                        val scope = checkPreconditionNotNull(scrollObservationScopes.findById(id)) {
                             "scroll observation scope does not exist"
                         }
                         scope.horizontalScrollAxisRange = newNode.unmergedConfig.getOrNull(
@@ -409,7 +410,7 @@
         val session = contentCaptureSession ?: return
         // TODO: consider having a `newContentCaptureId` function to improve readability.
         val autofillId = session.newAutofillId(id.toLong())
-        checkNotNull(autofillId) { "Invalid content capture ID" }
+        checkPreconditionNotNull(autofillId) { "Invalid content capture ID" }
         session.notifyViewTextChanged(autofillId, newText)
     }
 
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidClipboardManager.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidClipboardManager.android.kt
index fbdf0868..2c94b70 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidClipboardManager.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidClipboardManager.android.kt
@@ -86,10 +86,7 @@
         return clipboardManager.primaryClipDescription?.let(::ClipMetadata)
     }
 
-    override fun setClip(
-        clipEntry: ClipEntry,
-        clipMetadata: ClipMetadata?
-    ) {
+    override fun setClip(clipEntry: ClipEntry) {
         // We ignore the clipDescription parameter on Android because clipEntry comes with one.
         clipboardManager.setPrimaryClip(clipEntry.clipData)
     }
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 464ada3..064defc 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
@@ -141,6 +141,7 @@
 import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
 import androidx.compose.ui.input.rotary.RotaryScrollEvent
 import androidx.compose.ui.input.rotary.onRotaryScrollEvent
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.PlacementScope
 import androidx.compose.ui.layout.RootMeasurePolicy
@@ -1490,7 +1491,7 @@
 
         _inputModeManager.inputMode = if (isInTouchMode) Touch else Keyboard
 
-        val lifecycle = checkNotNull(viewTreeOwners?.lifecycleOwner?.lifecycle) {
+        val lifecycle = checkPreconditionNotNull(viewTreeOwners?.lifecycleOwner?.lifecycle) {
             "No lifecycle owner exists"
         }
         lifecycle.addObserver(this)
@@ -1510,7 +1511,7 @@
     override fun onDetachedFromWindow() {
         super.onDetachedFromWindow()
         snapshotObserver.stopObserving()
-        val lifecycle = checkNotNull(viewTreeOwners?.lifecycleOwner?.lifecycle) {
+        val lifecycle = checkPreconditionNotNull(viewTreeOwners?.lifecycleOwner?.lifecycle) {
             "No lifecycle owner exists"
         }
         lifecycle.removeObserver(contentCaptureManager)
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 be75da7..5b3edaf 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
@@ -56,6 +56,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.toComposeRect
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.layout.boundsInParent
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.node.HitTestResult
@@ -446,7 +447,7 @@
         if (virtualViewId == AccessibilityNodeProviderCompat.HOST_VIEW_ID) {
             info.setParent(view.getParentForAccessibility() as? View)
         } else {
-            var parentId = checkNotNull(semanticsNode.parent?.id) {
+            var parentId = checkPreconditionNotNull(semanticsNode.parent?.id) {
                 "semanticsNode $virtualViewId has null parent"
             }
             if (parentId == view.semanticsOwner.unmergedRootSemanticsNode.id) {
@@ -2374,7 +2375,7 @@
             // We do doing this search because the new configuration is set as a whole, so we
             // can't indicate which property is changed when setting the new configuration.
             val oldNode = previousSemanticsNodes[id] ?: continue
-            val newNode = checkNotNull(newSemanticsNodes[id]?.semanticsNode) {
+            val newNode = checkPreconditionNotNull(newSemanticsNodes[id]?.semanticsNode) {
                 "no value for specified key"
             }
 
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidUriHandler.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidUriHandler.android.kt
index 714ba62..75d3d6e 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidUriHandler.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidUriHandler.android.kt
@@ -16,13 +16,24 @@
 
 package androidx.compose.ui.platform
 
+import android.content.ActivityNotFoundException
 import android.content.Context
 import android.content.Intent
 import android.net.Uri
 
-class AndroidUriHandler(private val context: Context) :
-    UriHandler {
+class AndroidUriHandler(private val context: Context) : UriHandler {
+
+    /**
+     * Open given URL in browser
+     *
+     * @throws IllegalArgumentException when given [uri] is invalid and/or can't be handled by the
+     * system
+     */
     override fun openUri(uri: String) {
-        context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(uri)))
+        try {
+            context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(uri)))
+        } catch (e: ActivityNotFoundException) {
+            throw IllegalArgumentException("Can't open $uri.", e)
+        }
     }
 }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewCompositionStrategy.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewCompositionStrategy.android.kt
index c79fcc1..ef2ede5 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewCompositionStrategy.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewCompositionStrategy.android.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.platform
 
 import android.view.View
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.platform.ViewCompositionStrategy.Companion.Default
 import androidx.customview.poolingcontainer.PoolingContainerListener
 import androidx.customview.poolingcontainer.addPoolingContainerListener
@@ -151,7 +152,7 @@
     object DisposeOnViewTreeLifecycleDestroyed : ViewCompositionStrategy {
         override fun installFor(view: AbstractComposeView): () -> Unit {
             if (view.isAttachedToWindow) {
-                val lco = checkNotNull(view.findViewTreeLifecycleOwner()) {
+                val lco = checkPreconditionNotNull(view.findViewTreeLifecycleOwner()) {
                     "View tree for $view has no ViewTreeLifecycleOwner"
                 }
                 return installForLifecycle(view, lco.lifecycle)
@@ -160,7 +161,7 @@
                 var disposer: () -> Unit
                 val listener = object : View.OnAttachStateChangeListener {
                     override fun onViewAttachedToWindow(v: View) {
-                        val lco = checkNotNull(view.findViewTreeLifecycleOwner()) {
+                        val lco = checkPreconditionNotNull(view.findViewTreeLifecycleOwner()) {
                             "View tree for $view has no ViewTreeLifecycleOwner"
                         }
                         disposer = installForLifecycle(view, lco.lifecycle)
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.android.kt
index 5f3ee8d..da10086 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.android.kt
@@ -35,6 +35,7 @@
 import androidx.compose.ui.MotionDurationScale
 import androidx.compose.ui.R
 import androidx.compose.ui.internal.checkPrecondition
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.core.os.HandlerCompat
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleEventObserver
@@ -350,7 +351,7 @@
     }
     val runRecomposeScope = CoroutineScope(contextWithClockAndMotionScale)
     val viewTreeLifecycle =
-        checkNotNull(lifecycle ?: findViewTreeLifecycleOwner()?.lifecycle) {
+        checkPreconditionNotNull(lifecycle ?: findViewTreeLifecycleOwner()?.lifecycle) {
             "ViewTreeLifecycleOwner not found from $this"
         }
 
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 22176eb..e24a9f2 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
@@ -37,6 +37,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.UiComposable
 import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.materialize
 import androidx.compose.ui.node.ComposeUiNode.Companion.SetCompositeKeyHash
 import androidx.compose.ui.node.ComposeUiNode.Companion.SetResolvedCompositionLocals
@@ -311,7 +312,7 @@
 @Suppress("UNCHECKED_CAST", "ExceptionMessage")
 private fun <T : View> LayoutNode.requireViewFactoryHolder(): ViewFactoryHolder<T> {
     @OptIn(InternalComposeUiApi::class)
-    return checkNotNull(interopViewFactoryHolder) as ViewFactoryHolder<T>
+    return checkPreconditionNotNull(interopViewFactoryHolder) as ViewFactoryHolder<T>
 }
 
 /**
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
index 352e3ec..28754ba 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
@@ -85,14 +85,26 @@
  * set to `false` for Android [R][Build.VERSION_CODES.R] and earlier.
  */
 @Immutable
-class DialogProperties constructor(
-    val dismissOnBackPress: Boolean = true,
-    val dismissOnClickOutside: Boolean = true,
+actual class DialogProperties constructor(
+    actual val dismissOnBackPress: Boolean = true,
+    actual val dismissOnClickOutside: Boolean = true,
     val securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
-    val usePlatformDefaultWidth: Boolean = true,
+    actual val usePlatformDefaultWidth: Boolean = true,
     val decorFitsSystemWindows: Boolean = true
 ) {
+    actual constructor(
+        dismissOnBackPress: Boolean,
+        dismissOnClickOutside: Boolean,
+        usePlatformDefaultWidth: Boolean,
+    ) : this(
+        dismissOnBackPress = dismissOnBackPress,
+        dismissOnClickOutside = dismissOnClickOutside,
+        securePolicy = SecureFlagPolicy.Inherit,
+        usePlatformDefaultWidth = usePlatformDefaultWidth,
+        decorFitsSystemWindows = true
+    )
 
+    @Deprecated("Maintained for binary compatibility", level = DeprecationLevel.HIDDEN)
     constructor(
         dismissOnBackPress: Boolean = true,
         dismissOnClickOutside: Boolean = true,
@@ -148,9 +160,9 @@
  * @param content The content to be displayed inside the dialog.
  */
 @Composable
-fun Dialog(
+actual fun Dialog(
     onDismissRequest: () -> Unit,
-    properties: DialogProperties = DialogProperties(),
+    properties: DialogProperties,
     content: @Composable () -> Unit
 ) {
     val view = LocalView.current
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt
index 5e60f37..818c02a 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt
@@ -113,15 +113,29 @@
  * the platform default, which is smaller than the screen width.
  */
 @Immutable
-class PopupProperties @ExperimentalComposeUiApi constructor(
-    val focusable: Boolean = false,
-    val dismissOnBackPress: Boolean = true,
-    val dismissOnClickOutside: Boolean = true,
+actual class PopupProperties @ExperimentalComposeUiApi constructor(
+    actual val focusable: Boolean = false,
+    actual val dismissOnBackPress: Boolean = true,
+    actual val dismissOnClickOutside: Boolean = true,
     val securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
     val excludeFromSystemGesture: Boolean = true,
-    val clippingEnabled: Boolean = true,
+    actual val clippingEnabled: Boolean = true,
     val usePlatformDefaultWidth: Boolean = false
 ) {
+    actual constructor(
+        focusable: Boolean,
+        dismissOnBackPress: Boolean,
+        dismissOnClickOutside: Boolean,
+        clippingEnabled: Boolean,
+    ) : this (
+        focusable = focusable,
+        dismissOnBackPress = dismissOnBackPress,
+        dismissOnClickOutside = dismissOnClickOutside,
+        securePolicy = SecureFlagPolicy.Inherit,
+        excludeFromSystemGesture = true,
+        clippingEnabled = clippingEnabled,
+    )
+
     @OptIn(ExperimentalComposeUiApi::class)
     constructor(
         focusable: Boolean = false,
@@ -189,11 +203,11 @@
  * @param content The content to be displayed inside the popup.
  */
 @Composable
-fun Popup(
-    alignment: Alignment = Alignment.TopStart,
-    offset: IntOffset = IntOffset(0, 0),
-    onDismissRequest: (() -> Unit)? = null,
-    properties: PopupProperties = PopupProperties(),
+actual fun Popup(
+    alignment: Alignment,
+    offset: IntOffset,
+    onDismissRequest: (() -> Unit)?,
+    properties: PopupProperties,
     content: @Composable () -> Unit
 ) {
     val popupPositioner = remember(alignment, offset) {
@@ -224,10 +238,10 @@
  * @param content The content to be displayed inside the popup.
  */
 @Composable
-fun Popup(
+actual fun Popup(
     popupPositionProvider: PopupPositionProvider,
-    onDismissRequest: (() -> Unit)? = null,
-    properties: PopupProperties = PopupProperties(),
+    onDismissRequest: (() -> Unit)?,
+    properties: PopupProperties,
     content: @Composable () -> Unit
 ) {
     val view = LocalView.current
@@ -410,7 +424,7 @@
 
     /** Track parent coordinates and content size; only show popup once we have both. */
     val canCalculatePosition by derivedStateOf {
-        parentLayoutCoordinates != null && popupContentSize != null
+        parentLayoutCoordinates?.takeIf { it.isAttached } != null && popupContentSize != null
     }
 
     // On systems older than Android S, there is a bug in the surface insets matrix math used by
@@ -674,7 +688,7 @@
      */
     @VisibleForTesting
     internal fun updateParentBounds() {
-        val coordinates = parentLayoutCoordinates ?: return
+        val coordinates = parentLayoutCoordinates?.takeIf { it.isAttached } ?: return
         val layoutSize = coordinates.size
 
         val position = coordinates.positionInWindow()
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/platform/ClipboardManagerTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/platform/ClipboardManagerTest.kt
index aad4c62..6c31ebf 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/platform/ClipboardManagerTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/platform/ClipboardManagerTest.kt
@@ -57,8 +57,5 @@
 
     override fun hasClip(): Boolean = false
 
-    override fun setClip(
-        clipEntry: ClipEntry,
-        clipMetadata: ClipMetadata?
-    ) {}
+    override fun setClip(clipEntry: ClipEntry) = Unit
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt
index 3c02269..dd440f1 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt
@@ -22,6 +22,7 @@
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.internal.JvmDefaultWithCompatibility
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.node.DrawModifierNode
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.Nodes
@@ -208,7 +209,7 @@
             cacheDrawScope.apply {
                 drawResult = null
                 observeReads { block() }
-                checkNotNull(drawResult) {
+                checkPreconditionNotNull(drawResult) {
                     "DrawResult not defined, did you forget to call onDraw?"
                 }
             }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
index 489b51e..45d9d34 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
@@ -23,6 +23,7 @@
 import androidx.compose.ui.focus.FocusStateImpl.ActiveParent
 import androidx.compose.ui.focus.FocusStateImpl.Captured
 import androidx.compose.ui.focus.FocusStateImpl.Inactive
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.layout.BeyondBoundsLayout
 import androidx.compose.ui.layout.ModifierLocalBeyondBoundsLayout
 import androidx.compose.ui.modifier.ModifierLocalModifierNode
@@ -174,7 +175,7 @@
 
     internal fun commitFocusState() {
         with(requireTransactionManager()) {
-            committedFocusState = checkNotNull(uncommittedFocusState) {
+            committedFocusState = checkPreconditionNotNull(uncommittedFocusState) {
                 "committing a node that was not updated in the current transaction"
             }
         }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactionManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactionManager.kt
index ff71b28..4a6878e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactionManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactionManager.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.focus
 
 import androidx.compose.runtime.collection.mutableVectorOf
+import androidx.compose.ui.internal.checkPreconditionNotNull
 
 /**
  * This manager provides a way to ensure that only one focus transaction is running at a time.
@@ -75,7 +76,7 @@
     var FocusTargetNode.uncommittedFocusState: FocusStateImpl?
         get() = states[this]
         set(value) {
-            states[this] = checkNotNull(value) { "requires a non-null focus state" }
+            states[this] = checkPreconditionNotNull(value) { "requires a non-null focus state" }
         }
 
     private fun beginTransaction() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/internal/InlineClassHelper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/internal/InlineClassHelper.kt
index 334b0f7..b335162 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/internal/InlineClassHelper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/internal/InlineClassHelper.kt
@@ -22,11 +22,11 @@
 // This function exists so we do *not* inline the throw. It keeps
 // the call site much smaller and since it's the slow path anyway,
 // we don't mind the extra function call
-internal fun throwIllegalStateException(message: String) {
+internal fun throwIllegalStateException(message: String): Nothing {
     throw IllegalStateException(message)
 }
 
-internal fun throwIllegalArgumentException(message: String) {
+internal fun throwIllegalArgumentException(message: String): Nothing {
     throw IllegalArgumentException(message)
 }
 
@@ -67,8 +67,22 @@
         throwIllegalStateException(lazyMessage())
     }
 
-    // We can't be null, we would have thrown earlier
-    return value!!
+    return value
+}
+
+// Like Kotlin's checkNotNull() but with a non-inline throw
+@Suppress("NOTHING_TO_INLINE", "BanInlineOptIn")
+@OptIn(ExperimentalContracts::class)
+internal inline fun <T : Any> checkPreconditionNotNull(value: T?): T {
+    contract {
+        returns() implies (value != null)
+    }
+
+    if (value == null) {
+        throwIllegalStateException("Required value was null.")
+    }
+
+    return value
 }
 
 // Like Kotlin's require() but without the .toString() call
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/IntermediateLayoutModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/IntermediateLayoutModifierNode.kt
index 6f790e9..af32b35 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/IntermediateLayoutModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/IntermediateLayoutModifierNode.kt
@@ -19,6 +19,7 @@
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.GraphicsLayerScope
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.node.LayoutModifierNode
 import androidx.compose.ui.node.NodeMeasuringIntrinsics
 import androidx.compose.ui.node.Nodes
@@ -84,7 +85,7 @@
 
     override fun onAttach() {
         val coordinates = coordinator?.lookaheadDelegate?.lookaheadLayoutCoordinates
-        checkNotNull(coordinates) { "could not fetch lookahead coordinates" }
+        checkPreconditionNotNull(coordinates) { "could not fetch lookahead coordinates" }
 
         val closestLookaheadRoot = requireLayoutNode().lookaheadRoot
         closestLookaheadScope = if (closestLookaheadRoot?.isVirtualLookaheadRoot == true) {
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 36281ec..8bd59f3 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
@@ -27,6 +27,7 @@
 import androidx.compose.ui.input.pointer.PointerInputFilter
 import androidx.compose.ui.input.pointer.PointerInputModifier
 import androidx.compose.ui.internal.checkPrecondition
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.internal.requirePrecondition
 import androidx.compose.ui.layout.IntrinsicMeasurable
 import androidx.compose.ui.layout.IntrinsicMeasureScope
@@ -504,7 +505,7 @@
      */
     internal fun detach() {
         val owner = owner
-        checkNotNull(owner) {
+        checkPreconditionNotNull(owner) {
             "Cannot detach node that is already detached!  Tree: " + parent?.debugTreeToString()
         }
         invalidateFocusOnDetach()
@@ -820,7 +821,7 @@
             }
             val layerCoordinator = _innerLayerCoordinator
             if (layerCoordinator != null) {
-                checkNotNull(layerCoordinator.layer) { "layer was not set" }
+                checkPreconditionNotNull(layerCoordinator.layer) { "layer was not set" }
             }
             return layerCoordinator
         }
@@ -1466,7 +1467,7 @@
  */
 internal fun LayoutNode.requireOwner(): Owner {
     val owner = owner
-    checkNotNull(owner) {
+    checkPreconditionNotNull(owner) {
         "LayoutNode should be attached to an owner"
     }
     return owner
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 fe78904..282375b 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
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.collection.MutableVector
 import androidx.compose.ui.graphics.GraphicsLayerScope
 import androidx.compose.ui.internal.checkPrecondition
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.internal.requirePrecondition
 import androidx.compose.ui.layout.AlignmentLine
 import androidx.compose.ui.layout.Measurable
@@ -975,8 +976,10 @@
          */
         fun measureBasedOnLookahead() {
             val lookaheadDelegate = lookaheadPassDelegate
-            val parent = checkNotNull(layoutNode.parent) { "layoutNode parent is not set" }
-            checkNotNull(lookaheadDelegate) { "invalid lookaheadDelegate" }
+            val parent = checkPreconditionNotNull(layoutNode.parent) {
+                "layoutNode parent is not set"
+            }
+            checkPreconditionNotNull(lookaheadDelegate) { "invalid lookaheadDelegate" }
             if (lookaheadDelegate.measuredByParent == LayoutNode.UsageByParent.InMeasureBlock &&
                 parent.layoutState == LayoutState.Measuring
             ) {
@@ -994,7 +997,7 @@
          * layerBlock as lookahead.
          */
         fun placeBasedOnLookahead() {
-            val lookaheadDelegate = checkNotNull(lookaheadPassDelegate) {
+            val lookaheadDelegate = checkPreconditionNotNull(lookaheadPassDelegate) {
                 "invalid lookaheadDelegate"
             }
             placeAt(
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 8486e18..4fbe097 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
@@ -25,6 +25,7 @@
 import androidx.compose.ui.areObjectsOfSameType
 import androidx.compose.ui.input.pointer.SuspendPointerInputElement
 import androidx.compose.ui.internal.checkPrecondition
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.layout.ModifierInfo
 
 private val SentinelHead = object : Modifier.Node() {
@@ -124,7 +125,7 @@
             // removed we will break into a structural update.
             var node: Modifier.Node? = paddedHead.child
             while (node != null && i < beforeSize) {
-                checkNotNull(before) { "expected prior modifier list to be non-empty" }
+                checkPreconditionNotNull(before) { "expected prior modifier list to be non-empty" }
                 val prev = before[i]
                 val next = after[i]
                 when (actionForModifiers(prev, next)) {
@@ -154,8 +155,8 @@
             }
             if (i < beforeSize) {
                 coordinatorSyncNeeded = true
-                checkNotNull(before) { "expected prior modifier list to be non-empty" }
-                checkNotNull(node) { "structuralUpdate requires a non-null tail" }
+                checkPreconditionNotNull(before) { "expected prior modifier list to be non-empty" }
+                checkPreconditionNotNull(node) { "structuralUpdate requires a non-null tail" }
                 // there must have been a structural change
                 // we only need to diff what is left of the list, so we use `i` to determine how
                 // much of the list is left.
@@ -184,7 +185,7 @@
             }
             syncAggregateChildKindSet()
         } else if (after.size == 0) {
-            checkNotNull(before) { "expected prior modifier list to be non-empty" }
+            checkPreconditionNotNull(before) { "expected prior modifier list to be non-empty" }
             // common case where we we are removing all the modifiers.
             var node = paddedHead.child
             while (node != null && i < before.size) {
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 510eef4..b884763 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
@@ -31,6 +31,7 @@
 import androidx.compose.ui.input.pointer.PointerInputModifier
 import androidx.compose.ui.input.rotary.RotaryInputModifierNode
 import androidx.compose.ui.internal.checkPrecondition
+import androidx.compose.ui.internal.checkPreconditionNotNull
 import androidx.compose.ui.layout.IntermediateLayoutModifierNode
 import androidx.compose.ui.layout.LayoutModifier
 import androidx.compose.ui.layout.OnGloballyPositionedModifier
@@ -323,7 +324,7 @@
 private object CanFocusChecker : FocusProperties {
     private var canFocusValue: Boolean? = null
     override var canFocus: Boolean
-        get() = checkNotNull(canFocusValue) { "canFocus is read before it is written" }
+        get() = checkPreconditionNotNull(canFocusValue) { "canFocus is read before it is written" }
         set(value) { canFocusValue = value }
     fun isCanFocusSet(): Boolean = canFocusValue != null
     fun reset() { canFocusValue = null }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/ClipboardManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/ClipboardManager.kt
index 0e49a61..9100166 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/ClipboardManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/ClipboardManager.kt
@@ -67,14 +67,9 @@
      * Puts the given [clipEntry] in platform's ClipboardManager.
      *
      * @param clipEntry Platform specific clip object that either holds data or links to it.
-     * @param clipMetadata Optional description of what is inside [clipEntry]. Some platforms like
-     * Android may have a [ClipEntry] contain its [ClipMetadata].
      */
     @Suppress("GetterSetterNames")
-    fun setClip(
-        clipEntry: ClipEntry,
-        clipMetadata: ClipMetadata? = null
-    ) = Unit
+    fun setClip(clipEntry: ClipEntry) = Unit
 
     /**
      * Returns true if there is currently a primary clip on the platform Clipboard. Even though
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/UriHandler.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/UriHandler.kt
index 104fc59..5f5047d 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/UriHandler.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/UriHandler.kt
@@ -22,6 +22,9 @@
 interface UriHandler {
     /**
      * Open given URL in browser
+     *
+     * @throws IllegalArgumentException when given [uri] is invalid and/or can't be handled by the
+     * system
      */
     fun openUri(uri: String)
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Dialog.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Dialog.kt
new file mode 100644
index 0000000..2acc6da
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Dialog.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.window
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+
+/**
+ * Properties used to customize the behavior of a [Dialog].
+ *
+ * @property dismissOnBackPress whether the popup can be dismissed by pressing the back button
+ *  * on Android or escape key on desktop.
+ * If true, pressing the back button will call onDismissRequest.
+ * @property dismissOnClickOutside whether the dialog can be dismissed by clicking outside the
+ * dialog's bounds. If true, clicking outside the dialog will call onDismissRequest.
+ * @property usePlatformDefaultWidth Whether the width of the dialog's content should be limited to
+ * the platform default, which is smaller than the screen width.
+ * **Might be used only as named argument**.
+ */
+@Immutable
+expect class DialogProperties(
+    dismissOnBackPress: Boolean = true,
+    dismissOnClickOutside: Boolean = true,
+    usePlatformDefaultWidth: Boolean = true,
+) {
+    val dismissOnBackPress: Boolean
+    val dismissOnClickOutside: Boolean
+    val usePlatformDefaultWidth: Boolean
+}
+
+/**
+ * Opens a dialog with the given content.
+ *
+ * A dialog is a small window that prompts the user to make a decision or enter
+ * additional information. A dialog does not fill the screen and is normally used
+ * for modal events that require users to take an action before they can proceed.
+ *
+ * The dialog is visible as long as it is part of the composition hierarchy.
+ * In order to let the user dismiss the Dialog, the implementation of [onDismissRequest] should
+ * contain a way to remove the dialog from the composition hierarchy.
+ *
+ * Example usage:
+ *
+ * @sample androidx.compose.ui.samples.DialogSample
+ *
+ * @param onDismissRequest Executes when the user tries to dismiss the dialog.
+ * @param properties [DialogProperties] for further customization of this dialog's behavior.
+ * @param content The content to be displayed inside the dialog.
+ */
+@Composable
+expect fun Dialog(
+    onDismissRequest: () -> Unit,
+    properties: DialogProperties = DialogProperties(),
+    content: @Composable () -> Unit
+)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Popup.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Popup.kt
index 1cee60c..d1b0afe 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Popup.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Popup.kt
@@ -16,14 +16,46 @@
 
 package androidx.compose.ui.window
 
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntRect
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 
 /**
+ * Properties used to customize the behavior of a [Popup].
+ *
+ * @property focusable Whether the popup is focusable. When true, the popup will receive IME
+ * events and key presses, such as when the back button is pressed.
+ * @property dismissOnBackPress Whether the popup can be dismissed by pressing the back button
+ * on Android or escape key on desktop.
+ * If true, pressing the back button will call onDismissRequest. Note that [focusable] must be
+ * set to true in order to receive key events such as the back button - if the popup is not
+ * focusable then this property does nothing.
+ * @property dismissOnClickOutside Whether the popup can be dismissed by clicking outside the
+ * popup's bounds. If true, clicking outside the popup will call onDismissRequest.
+ * @property clippingEnabled Whether to allow the popup window to extend beyond the bounds of the
+ * screen. By default the window is clipped to the screen boundaries. Setting this to false will
+ * allow windows to be accurately positioned.
+ * The default value is true.
+ */
+@Immutable
+expect class PopupProperties(
+    focusable: Boolean = false,
+    dismissOnBackPress: Boolean = true,
+    dismissOnClickOutside: Boolean = true,
+    clippingEnabled: Boolean = true,
+) {
+    val focusable: Boolean
+    val dismissOnBackPress: Boolean
+    val dismissOnClickOutside: Boolean
+    val clippingEnabled: Boolean
+}
+
+/**
  * Calculates the position of a [Popup] on screen.
  */
 @Immutable
@@ -86,3 +118,52 @@
             resolvedUserOffset
     }
 }
+
+/**
+ * Opens a popup with the given content.
+ *
+ * A popup is a floating container that appears on top of the current activity.
+ * It is especially useful for non-modal UI surfaces that remain hidden until they
+ * are needed, for example floating menus like Cut/Copy/Paste.
+ *
+ * The popup is positioned relative to its parent, using the [alignment] and [offset].
+ * The popup is visible as long as it is part of the composition hierarchy.
+ *
+ * @sample androidx.compose.ui.samples.PopupSample
+ *
+ * @param alignment The alignment relative to the parent.
+ * @param offset An offset from the original aligned position of the popup. Offset respects the
+ * Ltr/Rtl context, thus in Ltr it will be added to the original aligned position and in Rtl it
+ * will be subtracted from it.
+ * @param onDismissRequest Executes when the user clicks outside of the popup.
+ * @param properties [PopupProperties] for further customization of this popup's behavior.
+ * @param content The content to be displayed inside the popup.
+ */
+@Composable
+expect fun Popup(
+    alignment: Alignment = Alignment.TopStart,
+    offset: IntOffset = IntOffset(0, 0),
+    onDismissRequest: (() -> Unit)? = null,
+    properties: PopupProperties = @OptIn(ExperimentalComposeUiApi::class) PopupProperties(),
+    content: @Composable () -> Unit
+)
+
+/**
+ * Opens a popup with the given content.
+ *
+ * The popup is positioned using a custom [popupPositionProvider].
+ *
+ * @sample androidx.compose.ui.samples.PopupSample
+ *
+ * @param popupPositionProvider Provides the screen position of the popup.
+ * @param onDismissRequest Executes when the user clicks outside of the popup.
+ * @param properties [PopupProperties] for further customization of this popup's behavior.
+ * @param content The content to be displayed inside the popup.
+ */
+@Composable
+expect fun Popup(
+    popupPositionProvider: PopupPositionProvider,
+    onDismissRequest: (() -> Unit)? = null,
+    properties: PopupProperties = @OptIn(ExperimentalComposeUiApi::class) PopupProperties(),
+    content: @Composable () -> Unit
+)
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/PlatformClipboardManager.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/PlatformClipboardManager.desktop.kt
index fc8c17c..b909150 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/PlatformClipboardManager.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/PlatformClipboardManager.desktop.kt
@@ -63,10 +63,7 @@
         }
     }
 
-    override fun setClip(
-        clipEntry: ClipEntry,
-        clipMetadata: ClipMetadata?
-    ) {
+    override fun setClip(clipEntry: ClipEntry) {
         // Ignore clipDescription.
         systemClipboard?.setContents(clipEntry.transferable, null)
     }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopPopup.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopPopup.desktop.kt
index 17abbcd..7ad8bde 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopPopup.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopPopup.desktop.kt
@@ -17,6 +17,7 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -28,7 +29,11 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.awt.LocalLayerContainer
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.key.Key
 import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.type
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.LocalDensity
@@ -45,6 +50,35 @@
 import java.awt.MouseInfo
 import javax.swing.SwingUtilities.convertPointFromScreen
 
+@Immutable
+actual class PopupProperties @ExperimentalComposeUiApi actual constructor(
+    actual val focusable: Boolean,
+    actual val dismissOnBackPress: Boolean,
+    actual val dismissOnClickOutside: Boolean,
+    actual val clippingEnabled: Boolean,
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is PopupProperties) return false
+
+        if (focusable != other.focusable) return false
+        if (dismissOnBackPress != other.dismissOnBackPress) return false
+        if (dismissOnClickOutside != other.dismissOnClickOutside) return false
+        if (clippingEnabled != other.clippingEnabled) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = dismissOnBackPress.hashCode()
+        result = 31 * result + focusable.hashCode()
+        result = 31 * result + dismissOnBackPress.hashCode()
+        result = 31 * result + dismissOnClickOutside.hashCode()
+        result = 31 * result + clippingEnabled.hashCode()
+        return result
+    }
+}
+
 /**
  * Opens a popup with the given content.
  *
@@ -126,21 +160,102 @@
     content: @Composable () -> Unit
 ) {
     PopupLayout(
-        popupPositionProvider,
-        focusable,
-        onDismissRequest,
-        onPreviewKeyEvent,
-        onKeyEvent,
-        content
+        popupPositionProvider = popupPositionProvider,
+        focusable = focusable,
+        onDismissRequest = if (focusable) onDismissRequest else null,
+        onPreviewKeyEvent = onPreviewKeyEvent,
+        onKeyEvent = onKeyEvent,
+        content = content
     )
 }
 
-@OptIn(ExperimentalComposeUiApi::class)
+/**
+ * Opens a popup with the given content.
+ *
+ * A popup is a floating container that appears on top of the current activity.
+ * It is especially useful for non-modal UI surfaces that remain hidden until they
+ * are needed, for example floating menus like Cut/Copy/Paste.
+ *
+ * The popup is positioned relative to its parent, using the [alignment] and [offset].
+ * The popup is visible as long as it is part of the composition hierarchy.
+ *
+ * @sample androidx.compose.ui.samples.PopupSample
+ *
+ * @param alignment The alignment relative to the parent.
+ * @param offset An offset from the original aligned position of the popup. Offset respects the
+ * Ltr/Rtl context, thus in Ltr it will be added to the original aligned position and in Rtl it
+ * will be subtracted from it.
+ * @param onDismissRequest Executes when the user clicks outside of the popup.
+ * @param properties [PopupProperties] for further customization of this popup's behavior.
+ * @param content The content to be displayed inside the popup.
+ */
 @Composable
-private fun PopupLayout(
+actual fun Popup(
+    alignment: Alignment,
+    offset: IntOffset,
+    onDismissRequest: (() -> Unit)?,
+    properties: PopupProperties,
+    content: @Composable () -> Unit
+) {
+    val popupPositioner = remember(alignment, offset) {
+        AlignmentOffsetPositionProvider(
+            alignment,
+            offset
+        )
+    }
+
+    Popup(
+        popupPositionProvider = popupPositioner,
+        onDismissRequest = onDismissRequest,
+        properties = properties,
+        content = content
+    )
+}
+
+/**
+ * Opens a popup with the given content.
+ *
+ * The popup is positioned using a custom [popupPositionProvider].
+ *
+ * @sample androidx.compose.ui.samples.PopupSample
+ *
+ * @param popupPositionProvider Provides the screen position of the popup.
+ * @param onDismissRequest Executes when the user clicks outside of the popup.
+ * @param properties [PopupProperties] for further customization of this popup's behavior.
+ * @param content The content to be displayed inside the popup.
+ */
+@Composable
+actual fun Popup(
+    popupPositionProvider: PopupPositionProvider,
+    onDismissRequest: (() -> Unit)?,
+    properties: PopupProperties,
+    content: @Composable () -> Unit
+) {
+    PopupLayout(
+        popupPositionProvider,
+        properties.focusable,
+        if (properties.dismissOnClickOutside) onDismissRequest else null,
+        onKeyEvent = {
+            if (properties.dismissOnBackPress &&
+                it.type == KeyEventType.KeyDown && it.key == Key.Escape &&
+                onDismissRequest != null
+            ) {
+                onDismissRequest()
+                true
+            } else {
+                false
+            }
+        },
+        content = content
+    )
+}
+
+@Composable
+internal fun PopupLayout(
     popupPositionProvider: PopupPositionProvider,
     focusable: Boolean,
     onDismissRequest: (() -> Unit)?,
+    modifier: Modifier = Modifier,
     onPreviewKeyEvent: ((KeyEvent) -> Boolean) = { false },
     onKeyEvent: ((KeyEvent) -> Boolean) = { false },
     content: @Composable () -> Unit
@@ -183,6 +298,7 @@
         val composition = owner.setContent(parent = parentComposition) {
             Layout(
                 content = content,
+                modifier = modifier,
                 measurePolicy = { measurables, constraints ->
                     val width = constraints.maxWidth
                     val height = constraints.maxHeight
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Dialog.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Dialog.desktop.kt
index d224e73..4d9e671 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Dialog.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Dialog.desktop.kt
@@ -18,16 +18,26 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.currentCompositionLocalContext
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.awt.ComposeDialog
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.input.key.Key
 import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.type
 import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.ComponentUpdater
 import androidx.compose.ui.util.makeDisplayable
@@ -44,6 +54,113 @@
 import javax.swing.JDialog
 
 /**
+ * Properties used to customize the behavior of a [Dialog].
+ *
+ * @property dismissOnBackPress whether the popup can be dismissed by pressing the back button
+ *  * on Android or escape key on desktop.
+ * If true, pressing the back button will call onDismissRequest.
+ * @property dismissOnClickOutside whether the dialog can be dismissed by clicking outside the
+ * dialog's bounds. If true, clicking outside the dialog will call onDismissRequest.
+ * @property usePlatformDefaultWidth Whether the width of the dialog's content should be limited to
+ * the platform default, which is smaller than the screen width.
+ */
+@Immutable
+actual class DialogProperties actual constructor(
+    actual val dismissOnBackPress: Boolean,
+    actual val dismissOnClickOutside: Boolean,
+    actual val usePlatformDefaultWidth: Boolean,
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is DialogProperties) return false
+
+        if (dismissOnBackPress != other.dismissOnBackPress) return false
+        if (dismissOnClickOutside != other.dismissOnClickOutside) return false
+        if (usePlatformDefaultWidth != other.usePlatformDefaultWidth) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = dismissOnBackPress.hashCode()
+        result = 31 * result + dismissOnClickOutside.hashCode()
+        result = 31 * result + usePlatformDefaultWidth.hashCode()
+        return result
+    }
+}
+
+@Composable
+actual fun Dialog(
+    onDismissRequest: () -> Unit,
+    properties: DialogProperties,
+    content: @Composable () -> Unit
+) {
+    val popupPositioner = remember {
+        AlignmentOffsetPositionProvider(
+            alignment = Alignment.Center,
+            offset = IntOffset(0, 0)
+        )
+    }
+    PopupLayout(
+        popupPositionProvider = popupPositioner,
+        focusable = true,
+        if (properties.dismissOnClickOutside) onDismissRequest else null,
+        modifier = Modifier.drawBehind {
+            drawRect(Color.Black.copy(alpha = 0.6f))
+        },
+        onKeyEvent = {
+            if (properties.dismissOnBackPress &&
+                it.type == KeyEventType.KeyDown && it.key == Key.Escape
+            ) {
+                onDismissRequest()
+                true
+            } else {
+                false
+            }
+        },
+        content = content
+    )
+}
+
+@Deprecated(
+    message = "Replaced by DialogWindow",
+    replaceWith = ReplaceWith("DialogWindow(" +
+        "onCloseRequest, state, visible, title, icon, undecorated, transparent, resizable, " +
+        "enabled, focusable, onPreviewKeyEvent, onKeyEvent, content" +
+        ")")
+)
+@Composable
+fun Dialog(
+    onCloseRequest: () -> Unit,
+    state: DialogState = rememberDialogState(),
+    visible: Boolean = true,
+    title: String = "Untitled",
+    icon: Painter? = null,
+    undecorated: Boolean = false,
+    transparent: Boolean = false,
+    resizable: Boolean = true,
+    enabled: Boolean = true,
+    focusable: Boolean = true,
+    onPreviewKeyEvent: ((KeyEvent) -> Boolean) = { false },
+    onKeyEvent: ((KeyEvent) -> Boolean) = { false },
+    content: @Composable DialogWindowScope.() -> Unit
+) = DialogWindow(
+    onCloseRequest,
+    state,
+    visible,
+    title,
+    icon,
+    undecorated,
+    transparent,
+    resizable,
+    enabled,
+    focusable,
+    onPreviewKeyEvent,
+    onKeyEvent,
+    content
+)
+
+/**
  * Composes platform dialog in the current composition. When Dialog enters the composition,
  * a new platform dialog will be created and receives the focus. When Dialog leaves the
  * composition, dialog will be disposed and closed.
@@ -101,7 +218,7 @@
  * @param content content of the dialog
  */
 @Composable
-fun Dialog(
+fun DialogWindow(
     onCloseRequest: () -> Unit,
     state: DialogState = rememberDialogState(),
     visible: Boolean = true,
@@ -130,7 +247,7 @@
 
     val updater = remember(::ComponentUpdater)
 
-    Dialog(
+    DialogWindow(
         visible = visible,
         onPreviewKeyEvent = onPreviewKeyEvent,
         onKeyEvent = onKeyEvent,
@@ -172,6 +289,31 @@
     )
 }
 
+@Deprecated(
+    message = "Replaced by DialogWindow",
+    replaceWith = ReplaceWith("DialogWindow(" +
+        "visible, onPreviewKeyEvent, onKeyEvent, create, dispose, update, contents" +
+        ")")
+)
+@Composable
+fun Dialog(
+    visible: Boolean = true,
+    onPreviewKeyEvent: ((KeyEvent) -> Boolean) = { false },
+    onKeyEvent: ((KeyEvent) -> Boolean) = { false },
+    create: () -> ComposeDialog,
+    dispose: (ComposeDialog) -> Unit,
+    update: (ComposeDialog) -> Unit = {},
+    content: @Composable DialogWindowScope.() -> Unit
+) = DialogWindow(
+    visible,
+    onPreviewKeyEvent,
+    onKeyEvent,
+    create,
+    dispose,
+    update,
+    content
+)
+
 // TODO(demin): fix mouse hover after opening a dialog.
 //  When we open a modal dialog, ComposeLayer/mouseExited will
 //  never be called for the parent window. See ./gradlew run3
@@ -217,7 +359,7 @@
 @OptIn(ExperimentalComposeUiApi::class)
 @Suppress("unused")
 @Composable
-fun Dialog(
+fun DialogWindow(
     visible: Boolean = true,
     onPreviewKeyEvent: ((KeyEvent) -> Boolean) = { false },
     onKeyEvent: ((KeyEvent) -> Boolean) = { false },
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/dialog/DialogTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/dialog/DialogTest.kt
index 1d526c76..1a2d84f 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/dialog/DialogTest.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/dialog/DialogTest.kt
@@ -43,7 +43,7 @@
 import androidx.compose.ui.sendKeyEvent
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogWindow
 import androidx.compose.ui.window.launchApplication
 import androidx.compose.ui.window.rememberDialogState
 import androidx.compose.ui.window.runApplicationTest
@@ -74,7 +74,7 @@
             }
 
             if (isOpen) {
-                Dialog(
+                DialogWindow(
                     create = ::createWindow,
                     dispose = ComposeDialog::dispose
                 ) {
@@ -109,7 +109,7 @@
             }
 
             if (isOpen) {
-                Dialog(
+                DialogWindow(
                     create = ::createWindow,
                     dispose = ComposeDialog::dispose,
                     update = { it.title = title }
@@ -136,7 +136,7 @@
         var window: ComposeDialog? = null
 
         launchApplication {
-            Dialog(onCloseRequest = ::exitApplication) {
+            DialogWindow(onCloseRequest = ::exitApplication) {
                 window = this.window
                 Box(Modifier.size(32.dp).background(Color.Red))
             }
@@ -156,7 +156,7 @@
 
         launchApplication {
             if (isOpen) {
-                Dialog(
+                DialogWindow(
                     onCloseRequest = {
                         isCloseCalled = true
                     }
@@ -190,12 +190,12 @@
         launchApplication {
             if (isOpen) {
                 if (isLoading) {
-                    Dialog(onCloseRequest = {}) {
+                    DialogWindow(onCloseRequest = {}) {
                         window1 = this.window
                         Box(Modifier.size(32.dp).background(Color.Red))
                     }
                 } else {
-                    Dialog(onCloseRequest = {}) {
+                    DialogWindow(onCloseRequest = {}) {
                         window2 = this.window
                         Box(Modifier.size(32.dp).background(Color.Blue))
                     }
@@ -227,12 +227,12 @@
 
         launchApplication {
             if (isOpen) {
-                Dialog(onCloseRequest = {}) {
+                DialogWindow(onCloseRequest = {}) {
                     window1 = this.window
                     Box(Modifier.size(32.dp).background(Color.Red))
                 }
 
-                Dialog(onCloseRequest = {}) {
+                DialogWindow(onCloseRequest = {}) {
                     window2 = this.window
                     Box(Modifier.size(32.dp).background(Color.Blue))
                 }
@@ -259,7 +259,7 @@
 
         launchApplication {
             if (isOpen) {
-                Dialog(
+                DialogWindow(
                     onCloseRequest = {},
                     state = rememberDialogState(
                         size = DpSize(600.dp, 600.dp),
@@ -269,7 +269,7 @@
                     Box(Modifier.size(32.dp).background(Color.Red))
 
                     if (isNestedOpen) {
-                        Dialog(
+                        DialogWindow(
                             onCloseRequest = {},
                             state = rememberDialogState(
                                 size = DpSize(300.dp, 300.dp),
@@ -315,7 +315,7 @@
         launchApplication {
             if (isOpen) {
                 CompositionLocalProvider(localTestValue provides testValue) {
-                    Dialog(
+                    DialogWindow(
                         onCloseRequest = {},
                         state = rememberDialogState(
                             size = DpSize(600.dp, 600.dp),
@@ -324,7 +324,7 @@
                         actualValue1 = localTestValue.current
                         Box(Modifier.size(32.dp).background(Color.Red))
 
-                        Dialog(
+                        DialogWindow(
                             onCloseRequest = {},
                             state = rememberDialogState(
                                 size = DpSize(300.dp, 300.dp),
@@ -359,7 +359,7 @@
 
         launchApplication {
             if (isOpen) {
-                Dialog(onCloseRequest = {}) {
+                DialogWindow(onCloseRequest = {}) {
                     DisposableEffect(Unit) {
                         initCount++
                         onDispose {
@@ -392,7 +392,7 @@
         }
 
         launchApplication {
-            Dialog(
+            DialogWindow(
                 onCloseRequest = ::exitApplication,
                 onPreviewKeyEvent = {
                     onPreviewKeyEventKeys.add(it.key)
@@ -445,7 +445,7 @@
         }
 
         launchApplication {
-            Dialog(
+            DialogWindow(
                 onCloseRequest = ::exitApplication,
                 onPreviewKeyEvent = {
                     onWindowPreviewKeyEventKeys.add(it.key)
@@ -531,7 +531,7 @@
         var isVisibleOnFirstDraw = false
 
         launchApplication {
-            Dialog(onCloseRequest = ::exitApplication) {
+            DialogWindow(onCloseRequest = ::exitApplication) {
                 if (!isComposed) {
                     isVisibleOnFirstComposition = window.isVisible
                     isComposed = true
diff --git a/core/core-ktx/src/main/java/androidx/core/util/Consumer.kt b/core/core-ktx/src/main/java/androidx/core/util/PlatformConsumer.kt
similarity index 98%
rename from core/core-ktx/src/main/java/androidx/core/util/Consumer.kt
rename to core/core-ktx/src/main/java/androidx/core/util/PlatformConsumer.kt
index c1d00aab..0c5d4b1 100644
--- a/core/core-ktx/src/main/java/androidx/core/util/Consumer.kt
+++ b/core/core-ktx/src/main/java/androidx/core/util/PlatformConsumer.kt
@@ -16,6 +16,7 @@
 
 // java.util.function.Consumer was added in API 24
 @file:RequiresApi(24)
+@file:JvmName("ConsumerKt")
 package androidx.core.util
 
 import androidx.annotation.RequiresApi
diff --git a/core/core-remoteviews/src/androidTest/java/androidx/core/widget/RemoteViewsCompatTest.kt b/core/core-remoteviews/src/androidTest/java/androidx/core/widget/RemoteViewsCompatTest.kt
index 84a9146..be975aa 100644
--- a/core/core-remoteviews/src/androidTest/java/androidx/core/widget/RemoteViewsCompatTest.kt
+++ b/core/core-remoteviews/src/androidTest/java/androidx/core/widget/RemoteViewsCompatTest.kt
@@ -411,8 +411,9 @@
 
     @Suppress("UNCHECKED_CAST")
     private fun <V : View> getListChildAt(position: Int): V {
-        return if (mUsingBackport) {
-            // When using RemoteViewsAdapter, an extra wrapper FrameLayout is added.
+        return if (mUsingBackport || mListView.getChildAt(position) is AppWidgetHostView) {
+            // When using RemoteViewsAdapter or RemoteCollectionItemsAdapter, an extra wrapper
+            // FrameLayout is added.
             (mListView.getChildAt(position) as ViewGroup).getChildAt(0) as V
         } else {
             mListView.getChildAt(position) as V
diff --git a/core/core-telecom/build.gradle b/core/core-telecom/build.gradle
index 4c8fbd1..e0148ab 100644
--- a/core/core-telecom/build.gradle
+++ b/core/core-telecom/build.gradle
@@ -35,7 +35,7 @@
     api(libs.guavaListenableFuture)
     implementation("androidx.annotation:annotation:1.4.0")
     // @OptIn annotations
-    api(project(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
     implementation("androidx.core:core:1.9.0")
     implementation(libs.kotlinCoroutinesCore)
     implementation(libs.kotlinCoroutinesGuava)
diff --git a/core/core-telecom/integration-tests/testapp/build.gradle b/core/core-telecom/integration-tests/testapp/build.gradle
index d831923..a998e35 100644
--- a/core/core-telecom/integration-tests/testapp/build.gradle
+++ b/core/core-telecom/integration-tests/testapp/build.gradle
@@ -50,7 +50,7 @@
     implementation('androidx.recyclerview:recyclerview:1.2.1')
 
     // Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
-    androidTestImplementation(project(":annotation:annotation-experimental"))
+    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testRunner)
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
index 47eaeb3..e15d2ad 100644
--- 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
@@ -37,6 +37,7 @@
      * Records the value in the order it was received.
      * @param t the input argument.
      */
+    @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") /* Avoid breaking named parameter compat */
     override fun accept(t: T) {
         lock.withLock {
             values.add(t)
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index b3803d1..57a13ef 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -2350,12 +2350,12 @@
     method public java.io.FileOutputStream startWrite() throws java.io.IOException;
   }
 
-  public interface Consumer<T> {
-    method public void accept(T!);
+  public fun interface Consumer<T> {
+    method public void accept(T value);
   }
 
-  @java.lang.FunctionalInterface public interface Function<T, R> {
-    method public R! apply(T!);
+  public fun interface Function<T, R> {
+    method public R apply(T value);
   }
 
   public class ObjectsCompat {
@@ -2416,8 +2416,8 @@
     method @RequiresApi(21) public static androidx.core.util.SizeFCompat toSizeFCompat(android.util.SizeF);
   }
 
-  public interface Supplier<T> {
-    method public T! get();
+  public fun interface Supplier<T> {
+    method public T get();
   }
 
   public class TypedValueCompat {
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 433ec8e..839dde9 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -2733,16 +2733,16 @@
     method public java.io.FileOutputStream startWrite() throws java.io.IOException;
   }
 
-  public interface Consumer<T> {
-    method public void accept(T!);
+  public fun interface Consumer<T> {
+    method public void accept(T value);
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DebugUtils {
     method public static void buildShortClassTag(Object!, StringBuilder!);
   }
 
-  @java.lang.FunctionalInterface public interface Function<T, R> {
-    method public R! apply(T!);
+  public fun interface Function<T, R> {
+    method public R apply(T value);
   }
 
   @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class LogWriter extends java.io.Writer {
@@ -2833,8 +2833,8 @@
     method @RequiresApi(21) public static androidx.core.util.SizeFCompat toSizeFCompat(android.util.SizeF);
   }
 
-  public interface Supplier<T> {
-    method public T! get();
+  public fun interface Supplier<T> {
+    method public T get();
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class TimeUtils {
diff --git a/core/core/build.gradle b/core/core/build.gradle
index 17d3ad1..2799274 100644
--- a/core/core/build.gradle
+++ b/core/core/build.gradle
@@ -22,7 +22,7 @@
     }
 
     api("androidx.annotation:annotation:1.6.0")
-    api(project(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
     api("androidx.lifecycle:lifecycle-runtime:2.6.2")
     api("androidx.versionedparcelable:versionedparcelable:1.1.1")
     implementation("androidx.collection:collection:1.0.0")
diff --git a/core/core/src/main/java/androidx/core/util/Consumer.java b/core/core/src/main/java/androidx/core/util/Consumer.kt
similarity index 75%
rename from core/core/src/main/java/androidx/core/util/Consumer.java
rename to core/core/src/main/java/androidx/core/util/Consumer.kt
index 28e0fb8..240f7e0 100644
--- a/core/core/src/main/java/androidx/core/util/Consumer.java
+++ b/core/core/src/main/java/androidx/core/util/Consumer.kt
@@ -13,19 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package androidx.core.util;
+package androidx.core.util
 
 /**
- * Compat version of {@link java.util.function.Consumer}
- * @param <T> the type of the input to the operation
+ * Compat version of [java.util.function.Consumer]
+ * @param T the type of the input to the operation
  */
-public interface Consumer<T> {
-
+fun interface Consumer<T> {
     /**
      * Performs this operation on the given argument.
      *
-     * @param t the input argument
+     * @param value the input argument
      */
-    void accept(T t);
+    fun accept(value: T)
 }
diff --git a/core/core/src/main/java/androidx/core/util/Function.java b/core/core/src/main/java/androidx/core/util/Function.kt
similarity index 70%
rename from core/core/src/main/java/androidx/core/util/Function.java
rename to core/core/src/main/java/androidx/core/util/Function.kt
index 682c961..04011c3 100644
--- a/core/core/src/main/java/androidx/core/util/Function.java
+++ b/core/core/src/main/java/androidx/core/util/Function.kt
@@ -13,20 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.core.util;
+package androidx.core.util
 
 /**
- * Compat version of {@link java.util.function.Function}
- * @param <T> the type of the input to the operation
- * @param <R>: the type of the output of the function
+ * Compat version of [java.util.function.Function]
+ * @param T the type of the input to the operation
+ * @param R the type of the output of the function
  */
-@FunctionalInterface
-public interface Function<T, R> {
+fun interface Function<T, R> {
     /**
      * Applies the function to the argument parameter.
      *
-     * @param t the argument for the function
+     * @param value the argument for the function
      * @return the result after applying function
      */
-    R apply(T t);
+    fun apply(value: T): R
 }
diff --git a/core/core/src/main/java/androidx/core/util/Supplier.java b/core/core/src/main/java/androidx/core/util/Supplier.kt
similarity index 78%
rename from core/core/src/main/java/androidx/core/util/Supplier.java
rename to core/core/src/main/java/androidx/core/util/Supplier.kt
index 9deba96..2e0bf69 100644
--- a/core/core/src/main/java/androidx/core/util/Supplier.java
+++ b/core/core/src/main/java/androidx/core/util/Supplier.kt
@@ -13,19 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package androidx.core.util;
+package androidx.core.util
 
 /**
- * Compat version of {@link java.util.function.Supplier}
- * @param <T> the type of the input to the operation
+ * Compat version of [java.util.function.Supplier]
+ * @param T the type of the input to the operation
  */
-public interface Supplier<T> {
-
+fun interface Supplier<T> {
     /**
      * Gets a result.
      *
      * @return a result
      */
-    T get();
+    fun get(): T
 }
diff --git a/credentials/credentials/api/current.txt b/credentials/credentials/api/current.txt
index 9ff0cd8..35fbc3b 100644
--- a/credentials/credentials/api/current.txt
+++ b/credentials/credentials/api/current.txt
@@ -754,7 +754,9 @@
 
   public abstract class CredentialEntry {
     method public static final androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
+    method public final CharSequence? getAffiliatedDomain();
     method public final androidx.credentials.provider.BeginGetCredentialOption getBeginGetCredentialOption();
+    property public final CharSequence? affiliatedDomain;
     property public final androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption;
     field public static final androidx.credentials.provider.CredentialEntry.Companion Companion;
   }
@@ -818,7 +820,8 @@
   }
 
   @RequiresApi(26) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
+    ctor @Deprecated public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
+    ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence? affiliatedDomain);
     method public static androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
     method public CharSequence? getDisplayName();
     method public android.graphics.drawable.Icon getIcon();
@@ -840,6 +843,7 @@
   public static final class PasswordCredentialEntry.Builder {
     ctor public PasswordCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption);
     method public androidx.credentials.provider.PasswordCredentialEntry build();
+    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAffiliatedDomain(CharSequence? affiliatedDomain);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDisplayName(CharSequence? displayName);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
diff --git a/credentials/credentials/api/restricted_current.txt b/credentials/credentials/api/restricted_current.txt
index 9ff0cd8..35fbc3b 100644
--- a/credentials/credentials/api/restricted_current.txt
+++ b/credentials/credentials/api/restricted_current.txt
@@ -754,7 +754,9 @@
 
   public abstract class CredentialEntry {
     method public static final androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
+    method public final CharSequence? getAffiliatedDomain();
     method public final androidx.credentials.provider.BeginGetCredentialOption getBeginGetCredentialOption();
+    property public final CharSequence? affiliatedDomain;
     property public final androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption;
     field public static final androidx.credentials.provider.CredentialEntry.Companion Companion;
   }
@@ -818,7 +820,8 @@
   }
 
   @RequiresApi(26) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
+    ctor @Deprecated public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
+    ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence? affiliatedDomain);
     method public static androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
     method public CharSequence? getDisplayName();
     method public android.graphics.drawable.Icon getIcon();
@@ -840,6 +843,7 @@
   public static final class PasswordCredentialEntry.Builder {
     ctor public PasswordCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption);
     method public androidx.credentials.provider.PasswordCredentialEntry build();
+    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAffiliatedDomain(CharSequence? affiliatedDomain);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDisplayName(CharSequence? displayName);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialEntryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialEntryTest.kt
index 1317f19..7e51e51 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialEntryTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialEntryTest.kt
@@ -54,6 +54,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION")
     fun createFrom_passwordCredential() {
         val entry = PasswordCredentialEntry(
             mContext,
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryJavaTest.java
index c718e47..12bb685 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryJavaTest.java
@@ -53,6 +53,8 @@
     private static final CharSequence USERNAME = "title";
     private static final CharSequence DISPLAYNAME = "subtitle";
     private static final CharSequence TYPE_DISPLAY_NAME = "Password";
+
+    private static final String AFFILIATED_DOMAIN = "affiliation-name";
     private static final Long LAST_USED_TIME = 10L;
 
     private static final boolean IS_AUTO_SELECT_ALLOWED = true;
@@ -159,6 +161,66 @@
     }
 
     @Test
+    public void constructor_defaultAffiliatedDomain() {
+        PasswordCredentialEntry entry = constructEntryWithRequiredParamsOnly();
+
+        assertThat(entry.getAffiliatedDomain()).isNull();
+    }
+
+    @Test
+    public void constructor_nonEmptyAffiliatedDomainSet_nonEmptyAffiliatedDomainRetrieved() {
+        String expectedAffiliatedDomain = "non-empty";
+
+        PasswordCredentialEntry entryWithAffiliatedDomain = new PasswordCredentialEntry(
+                mContext,
+                USERNAME,
+                mPendingIntent,
+                mBeginGetPasswordOption,
+                DISPLAYNAME,
+                Instant.ofEpochMilli(LAST_USED_TIME),
+                ICON,
+                false,
+                expectedAffiliatedDomain
+        );
+
+        assertThat(entryWithAffiliatedDomain.getAffiliatedDomain())
+                .isEqualTo(expectedAffiliatedDomain);
+    }
+
+    @Test
+    public void builder_constructDefault_containsOnlyDefaultValuesForSettableParameters() {
+        PasswordCredentialEntry entry = new PasswordCredentialEntry.Builder(mContext, USERNAME,
+                mPendingIntent, mBeginGetPasswordOption).build();
+
+        assertThat(entry.getAffiliatedDomain()).isNull();
+        assertThat(entry.getDisplayName()).isNull();
+        assertThat(entry.getLastUsedTime()).isNull();
+        assertThat(entry.isAutoSelectAllowed()).isFalse();
+    }
+
+    @Test
+    public void builder_setAffiliatedDomainNull_retrieveNullAffiliatedDomain() {
+        PasswordCredentialEntry entry = new PasswordCredentialEntry.Builder(mContext, USERNAME,
+                mPendingIntent, mBeginGetPasswordOption).setAffiliatedDomain(null).build();
+
+        assertThat(entry.getAffiliatedDomain()).isNull();
+    }
+
+    @Test
+    public void builder_setAffiliatedDomainNonNull_retrieveNonNullAffiliatedDomain() {
+        String expectedAffiliatedDomain = "affiliated-domain";
+
+        PasswordCredentialEntry entry = new PasswordCredentialEntry.Builder(
+                mContext,
+                USERNAME,
+                mPendingIntent,
+                mBeginGetPasswordOption
+        ).setAffiliatedDomain(expectedAffiliatedDomain).build();
+
+        assertThat(entry.getAffiliatedDomain()).isEqualTo(expectedAffiliatedDomain);
+    }
+
+    @Test
     @SdkSuppress(minSdkVersion = 28)
     public void fromSlice_requiredParams_success() {
         PasswordCredentialEntry originalEntry = constructEntryWithRequiredParamsOnly();
@@ -213,6 +275,7 @@
                 .setLastUsedTime(Instant.ofEpochMilli(LAST_USED_TIME))
                 .setIcon(ICON)
                 .setAutoSelectAllowed(IS_AUTO_SELECT_ALLOWED)
+                .setAffiliatedDomain(AFFILIATED_DOMAIN)
                 .build();
     }
 
@@ -221,6 +284,7 @@
         assertThat(USERNAME.equals(entry.getUsername()));
         assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
         assertThat(mBeginGetPasswordOption.getType()).isEqualTo(entry.getType());
+        assertThat(entry.getAffiliatedDomain()).isNull();
     }
 
     private void assertEntryWithAllParams(PasswordCredentialEntry entry) {
@@ -232,5 +296,6 @@
         assertThat(Instant.ofEpochMilli(LAST_USED_TIME)).isEqualTo(entry.getLastUsedTime());
         assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
         assertThat(mBeginGetPasswordOption.getType()).isEqualTo(entry.getType());
+        assertThat(entry.getAffiliatedDomain()).isEqualTo(AFFILIATED_DOMAIN);
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryTest.kt
index f3762f4..b99ef3b 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryTest.kt
@@ -70,6 +70,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION")
     fun constructor_emptyUsername_throwsIAE() {
         assertThrows(
             "Expected empty username to throw IllegalArgumentException",
@@ -95,6 +96,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION")
     fun constructor_nullTypeDisplayName_defaultDisplayNameSet() {
         val entry = PasswordCredentialEntry(
             mContext, USERNAME, mPendingIntent, BEGIN_OPTION)
@@ -116,6 +118,71 @@
     }
 
     @Test
+    fun constructor_defaultAffiliatedDomain() {
+        val defaultEntry = constructEntryWithRequiredParamsOnly()
+
+        assertThat(defaultEntry.affiliatedDomain).isNull()
+    }
+
+    @Test
+    fun constructor_nonEmptyAffiliatedDomainSet_nonEmptyAffiliatedDomainRetrieved() {
+        val expectedAffiliatedDomain = "non-empty"
+
+        val entryWithAffiliationType = PasswordCredentialEntry(
+            mContext,
+            USERNAME,
+            mPendingIntent,
+            BEGIN_OPTION,
+            DISPLAYNAME,
+            LAST_USED_TIME,
+            ICON,
+            affiliatedDomain = expectedAffiliatedDomain
+        )
+
+        assertThat(entryWithAffiliationType.affiliatedDomain).isEqualTo(expectedAffiliatedDomain)
+    }
+
+    @Test
+    fun builder_constructDefault_containsOnlyDefaultValuesForSettableParameters() {
+        val entry = PasswordCredentialEntry.Builder(
+            mContext,
+            USERNAME,
+            mPendingIntent,
+            BEGIN_OPTION
+        ).build()
+
+        assertThat(entry.affiliatedDomain).isNull()
+        assertThat(entry.displayName).isNull()
+        assertThat(entry.lastUsedTime).isNull()
+        assertThat(entry.isAutoSelectAllowed).isFalse()
+    }
+
+    @Test
+    fun builder_setAffiliatedDomainNull_retrieveNullAffiliatedDomain() {
+        val entry = PasswordCredentialEntry.Builder(
+            mContext,
+            USERNAME,
+            mPendingIntent,
+            BEGIN_OPTION
+        ).setAffiliatedDomain(null).build()
+
+        assertThat(entry.affiliatedDomain).isNull()
+    }
+
+    @Test
+    fun builder_setAffiliatedDomainNonNull_retrieveNonNullAffiliatedDomain() {
+        val expectedAffiliatedDomain = "name"
+        val entry = PasswordCredentialEntry.Builder(
+            mContext,
+            USERNAME,
+            mPendingIntent,
+            BEGIN_OPTION
+        ).setAffiliatedDomain(expectedAffiliatedDomain).build()
+
+        assertThat(entry.affiliatedDomain).isEqualTo(expectedAffiliatedDomain)
+    }
+
+    @Test
     @SdkSuppress(minSdkVersion = 34)
     fun fromSlice_success() {
         val originalEntry = constructEntryWithAllParams()
@@ -150,6 +217,7 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     private fun constructEntryWithRequiredParamsOnly(): PasswordCredentialEntry {
         return PasswordCredentialEntry(
             mContext,
@@ -167,13 +235,16 @@
             BEGIN_OPTION,
             DISPLAYNAME,
             LAST_USED_TIME,
-            ICON
+            ICON,
+            IS_AUTO_SELECT_ALLOWED,
+            AFFILIATED_DOMAIN
         )
     }
 
     private fun assertEntryWithRequiredParamsOnly(entry: PasswordCredentialEntry) {
         assertThat(USERNAME == entry.username)
         assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+        assertThat(entry.affiliatedDomain).isNull()
     }
 
     private fun assertEntryWithAllParams(entry: PasswordCredentialEntry) {
@@ -187,6 +258,8 @@
                 it.toEpochMilli())
         }
         assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+        assertThat(entry.isAutoSelectAllowed).isEqualTo(IS_AUTO_SELECT_ALLOWED)
+        assertThat(entry.affiliatedDomain).isEqualTo(AFFILIATED_DOMAIN)
     }
 
     companion object {
@@ -202,5 +275,7 @@
                 100, 100, Bitmap.Config.ARGB_8888
             )
         )
+        private val IS_AUTO_SELECT_ALLOWED = false
+        private val AFFILIATED_DOMAIN = "affiliation-name"
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/UiUtils.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/UiUtils.kt
index 65cc56d..fe563fa 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/UiUtils.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/UiUtils.kt
@@ -79,6 +79,7 @@
          * provider objects.
          */
         @JvmStatic
+        @Suppress("DEPRECATION")
         fun constructPasswordCredentialEntryDefault(username: CharSequence): CredentialEntry {
             return PasswordCredentialEntry(
                 sContext,
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialEntry.kt
index 046f906..498f9b1 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialEntry.kt
@@ -30,7 +30,8 @@
 abstract class CredentialEntry internal constructor(
     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     open val type: String,
-    val beginGetCredentialOption: BeginGetCredentialOption
+    val beginGetCredentialOption: BeginGetCredentialOption,
+    val affiliatedDomain: CharSequence? = null,
 ) {
 
     @RequiresApi(34)
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt
index 0c8eba0..d7550f9 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt
@@ -78,11 +78,14 @@
     val icon: Icon,
     val isAutoSelectAllowed: Boolean,
     beginGetPasswordOption: BeginGetPasswordOption,
+    affiliatedDomain: CharSequence? = null,
     private val autoSelectAllowedFromOption: Boolean = false,
-    private val isDefaultIcon: Boolean = false
+    private val isDefaultIcon: Boolean = false,
+
 ) : CredentialEntry(
     PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
-    beginGetPasswordOption
+    beginGetPasswordOption,
+    affiliatedDomain
 ) {
     init {
         require(username.isNotEmpty()) { "username must not be empty" }
@@ -91,6 +94,63 @@
     /**
      * @constructor constructs an instance of [PasswordCredentialEntry]
      *
+     * The [affiliatedDomain] parameter is filled if you provide a credential
+     * that is not directly associated with the requesting entity, but rather originates from an
+     * entity that is determined as being associated with the requesting entity through mechanisms
+     * such as digital asset links.
+     *
+     * @param context the context of the calling app, required to retrieve fallback resources
+     * @param username the username of the account holding the password credential
+     * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
+     * entry, must be created with flag [PendingIntent.FLAG_MUTABLE] to allow the Android
+     * system to attach the final request
+     * @param beginGetPasswordOption the option from the original [BeginGetCredentialResponse],
+     * for which this credential entry is being added
+     * @param displayName the displayName of the account holding the password credential
+     * @param lastUsedTime the last used time the credential underlying this entry was
+     * used by the user, distinguishable up to the milli second mark only such that if two
+     * entries have the same millisecond precision, they will be considered to have been used at
+     * the same time
+     * @param icon the icon to be displayed with this entry on the selector, if not set, a
+     * default icon representing a password credential type is set by the library
+     * @param isAutoSelectAllowed whether this entry is allowed to be auto
+     * selected if it is the only one on the UI, only takes effect if the app requesting for
+     * credentials also opts for auto select
+     * @param affiliatedDomain the user visible affiliated domain, a CharSequence
+     * representation of a web domain or an app package name that the given credential in this
+     * entry is associated with when it is different from the requesting entity, default null
+     *
+     * @throws IllegalArgumentException if [username] is empty
+     * @throws NullPointerException If [context], [username], [pendingIntent], or
+     * [beginGetPasswordOption] is null
+     */
+    constructor(
+        context: Context,
+        username: CharSequence,
+        pendingIntent: PendingIntent,
+        beginGetPasswordOption: BeginGetPasswordOption,
+        displayName: CharSequence? = null,
+        lastUsedTime: Instant? = null,
+        icon: Icon = Icon.createWithResource(context, R.drawable.ic_password),
+        isAutoSelectAllowed: Boolean = false,
+        affiliatedDomain: CharSequence? = null,
+    ) : this(
+        username,
+        displayName,
+        typeDisplayName = context.getString(
+            R.string.android_credentials_TYPE_PASSWORD_CREDENTIAL
+        ),
+        pendingIntent,
+        lastUsedTime,
+        icon,
+        isAutoSelectAllowed,
+        beginGetPasswordOption,
+        affiliatedDomain,
+    )
+
+    /**
+     * @constructor constructs an instance of [PasswordCredentialEntry]
+     *
      * @param context the context of the calling app, required to retrieve fallback resources
      * @param username the username of the account holding the password credential
      * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
@@ -113,6 +173,7 @@
      * @throws NullPointerException If [context], [username], [pendingIntent], or
      * [beginGetPasswordOption] is null
      */
+    @Deprecated("The constructor containing the affiliatedDomain bit should be utilized instead.")
     constructor(
         context: Context,
         username: CharSequence,
@@ -121,7 +182,7 @@
         displayName: CharSequence? = null,
         lastUsedTime: Instant? = null,
         icon: Icon = Icon.createWithResource(context, R.drawable.ic_password),
-        isAutoSelectAllowed: Boolean = false
+        isAutoSelectAllowed: Boolean = false,
     ) : this(
         username,
         displayName,
@@ -161,6 +222,7 @@
             val icon = entry.icon
             val isAutoSelectAllowed = entry.isAutoSelectAllowed
             val beginGetPasswordCredentialOption = entry.beginGetCredentialOption
+            val affiliatedDomain = entry.affiliatedDomain
 
             val autoSelectAllowed = if (isAutoSelectAllowed) {
                 AUTO_SELECT_TRUE_STRING
@@ -197,6 +259,10 @@
                     icon, /*subType=*/null,
                     listOf(SLICE_HINT_ICON)
                 )
+                .addText(
+                    affiliatedDomain, /*subType=*/null,
+                    listOf(SLICE_HINT_AFFILIATED_DOMAIN)
+                )
             try {
                 if (icon.resId == R.drawable.ic_password) {
                     sliceBuilder.addInt(
@@ -255,6 +321,7 @@
             var autoSelectAllowedFromOption = false
             var beginGetPasswordOptionId: CharSequence? = null
             var isDefaultIcon = false
+            var affiliatedDomain: CharSequence? = null
 
             slice.items.forEach {
                 if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
@@ -280,6 +347,8 @@
                     autoSelectAllowedFromOption = true
                 } else if (it.hasHint(SLICE_HINT_DEFAULT_ICON_RES_ID)) {
                     isDefaultIcon = true
+                } else if (it.hasHint(SLICE_HINT_AFFILIATED_DOMAIN)) {
+                    affiliatedDomain = it.text
                 }
             }
 
@@ -296,6 +365,7 @@
                         Bundle(),
                         beginGetPasswordOptionId!!.toString()
                     ),
+                    affiliatedDomain,
                     autoSelectAllowedFromOption,
                     isDefaultIcon
                 )
@@ -339,6 +409,9 @@
         private const val SLICE_HINT_AUTO_SELECT_FROM_OPTION =
             "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_SELECT_FROM_OPTION"
 
+        private const val SLICE_HINT_AFFILIATED_DOMAIN =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AFFILIATED_DOMAIN"
+
         private const val AUTO_SELECT_TRUE_STRING = "true"
 
         private const val AUTO_SELECT_FALSE_STRING = "false"
@@ -423,14 +496,15 @@
         private var lastUsedTime: Instant? = null
         private var icon: Icon? = null
         private var autoSelectAllowed = false
+        private var affiliatedDomain: CharSequence? = null
 
-        /** Sets a displayName to be shown on the UI with this entry */
+        /** Sets a displayName to be shown on the UI with this entry. */
         fun setDisplayName(displayName: CharSequence?): Builder {
             this.displayName = displayName
             return this
         }
 
-        /** Sets the icon to be shown on the UI with this entry */
+        /** Sets the icon to be shown on the UI with this entry. */
         fun setIcon(icon: Icon): Builder {
             this.icon = icon
             return this
@@ -438,7 +512,7 @@
 
         /**
          * Sets whether the entry should be auto-selected.
-         * The value is false by default
+         * The value is false by default.
          */
         @Suppress("MissingGetterMatchingBuilder")
         fun setAutoSelectAllowed(autoSelectAllowed: Boolean): Builder {
@@ -447,6 +521,17 @@
         }
 
         /**
+         * Sets whether the entry should have an affiliated domain, a CharSequence
+         * representation of some larger entity that may be used to bind multiple entries together
+         * (e.g. app_one, and app_two may be bound by 'super_app' as the larger affiliation
+         * domain) without length limit, default null.
+         */
+        fun setAffiliatedDomain(affiliatedDomain: CharSequence?): Builder {
+            this.affiliatedDomain = affiliatedDomain
+            return this
+        }
+
+        /**
          * Sets the last used time of this account. This information will be used to sort the
          * entries on the selector.
          */
@@ -471,7 +556,8 @@
                 lastUsedTime,
                 icon!!,
                 autoSelectAllowed,
-                beginGetPasswordOption
+                beginGetPasswordOption,
+                affiliatedDomain
             )
         }
     }
diff --git a/development/publishScan.sh b/development/publishScan.sh
index 5b38eb8..8845567 100755
--- a/development/publishScan.sh
+++ b/development/publishScan.sh
@@ -67,7 +67,7 @@
   rm -f "$log"
   echo
   echo uploading build scan
-  ./gradlew buildScanPublishPrevious
+  ./gradlew :buildScanPublishPrevious
   sleep 2
   if cat "$log" 2>/dev/null; then
     echo upload failed
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index b4d786e..76665cd1 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -246,31 +246,31 @@
     docs("androidx.media2:media2-widget:1.3.0")
     docs("androidx.media:media:1.7.0")
     // androidx.media3 is not hosted in androidx
-    docsWithoutApiSince("androidx.media3:media3-cast:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-common:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-container:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-database:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-datasource:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-datasource-cronet:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-datasource-okhttp:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-datasource-rtmp:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-decoder:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-effect:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-dash:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-hls:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-ima:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-rtsp:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-smoothstreaming:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-workmanager:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-extractor:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-muxer:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-session:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-test-utils:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-test-utils-robolectric:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-transformer:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-ui:1.2.1")
-    docsWithoutApiSince("androidx.media3:media3-ui-leanback:1.2.1")
+    docsWithoutApiSince("androidx.media3:media3-cast:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-common:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-container:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-database:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-datasource:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-datasource-cronet:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-datasource-okhttp:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-datasource-rtmp:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-decoder:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-effect:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-dash:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-hls:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-ima:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-rtsp:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-smoothstreaming:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-workmanager:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-extractor:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-muxer:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-session:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-test-utils:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-test-utils-robolectric:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-transformer:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-ui:1.3.0-alpha01")
+    docsWithoutApiSince("androidx.media3:media3-ui-leanback:1.3.0-alpha01")
     docs("androidx.mediarouter:mediarouter:1.7.0-alpha01")
     docs("androidx.mediarouter:mediarouter-testing:1.7.0-alpha01")
     docs("androidx.metrics:metrics-performance:1.0.0-beta01")
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index 50f39df..a43b78d 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -22,6 +22,7 @@
     // ads-identifier is deprecated
     kmpDocs(project(":annotation:annotation"))
     docs(project(":annotation:annotation-experimental"))
+    docs(project(":annotation:annotation-replacewith"))
     docs(project(":appactions:builtintypes:builtintypes"))
     samples(project(":appactions:builtintypes:builtintypes:builtintypes-samples"))
     docs(project(":appactions:interaction:interaction-capabilities-communication"))
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerBodyAdapter.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerBodyAdapter.kt
index 8ad826c..ced02280 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerBodyAdapter.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerBodyAdapter.kt
@@ -61,7 +61,6 @@
                 EmojiViewHolder(context,
                     emojiCellWidth!!,
                     emojiCellHeight!!,
-                    layoutInflater,
                     stickyVariantProvider,
                     onEmojiPickedListener = { emojiViewItem ->
                         onEmojiPickedListener(emojiViewItem)
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerPopupView.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerPopupView.kt
new file mode 100644
index 0000000..dd90bdf
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerPopupView.kt
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.emoji2.emojipicker
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
+import android.widget.FrameLayout
+import android.widget.LinearLayout;
+import androidx.core.content.ContextCompat
+
+/** Popup view for emoji picker to show emoji variants. */
+internal class EmojiPickerPopupView @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet?,
+    defStyleAttr: Int = 0,
+    private val targetEmojiView: View,
+    private val variants: List<String>,
+    private val emojiViewOnClickListener: OnClickListener
+) :
+    FrameLayout(context, attrs, defStyleAttr) {
+    private val popupView: LinearLayout
+    private val layoutTemplate: LayoutTemplate
+
+    init {
+        popupView = inflate(context, R.layout.variant_popup, /* root= */ null)
+            .findViewById<LinearLayout>(R.id.variant_popup)
+
+        layoutTemplate = getLayoutTemplate(variants)
+        for (row in layoutTemplate.template) {
+            val rowLayout = LinearLayout(context).apply {
+                orientation = LinearLayout.HORIZONTAL
+                layoutParams = LinearLayout.LayoutParams(
+                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT
+                )
+            }
+            for (item in row) {
+                val cell = when (item) {
+                    in 1..variants.size ->
+                        EmojiView(context).apply {
+                            willDrawVariantIndicator = false
+                            emoji = variants[item - 1]
+                            setOnClickListener(emojiViewOnClickListener)
+                            if (item == 1) {
+                                // Hover on the first emoji in the popup
+                                popupView.post {
+                                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
+                                }
+                            }
+                        }
+
+                    0 -> EmojiView(context)
+
+                    else -> SkinToneCircleView(context).apply {
+                        paint = Paint().apply {
+                            color = ContextCompat.getColor(
+                                context, SKIN_TONE_COLOR_RES_IDS[item + 5])
+                            style = Paint.Style.FILL
+                        }
+                    }
+                }.apply {
+                    layoutParams = ViewGroup.LayoutParams(
+                        targetEmojiView.width, targetEmojiView.height)
+                }
+                rowLayout.addView(cell)
+            }
+            popupView.addView(rowLayout)
+        }
+        addView(popupView)
+    }
+
+    fun getPopupViewWidth(): Int {
+        return layoutTemplate.numberOfColumns * targetEmojiView.width +
+            popupView.paddingStart + popupView.paddingEnd
+    }
+
+    fun getPopupViewHeight(): Int {
+        return layoutTemplate.numberOfRows * targetEmojiView.height +
+            popupView.paddingTop + popupView.paddingBottom
+    }
+
+    private fun getLayoutTemplate(variants: List<String>): LayoutTemplate {
+        val layout =
+            if (variants.size == SQUARE_LAYOUT_VARIANT_COUNT)
+                if (SQUARE_LAYOUT_EMOJI_NO_SKIN_TONE.contains(variants[0]))
+                    Layout.SQUARE
+                else Layout.SQUARE_WITH_SKIN_TONE_CIRCLE
+            else Layout.FLAT
+        var template = when (layout) {
+            Layout.SQUARE -> SQUARE_LAYOUT_TEMPLATE
+            Layout.SQUARE_WITH_SKIN_TONE_CIRCLE -> SQUARE_LAYOUT_WITH_SKIN_TONES_TEMPLATE
+            Layout.FLAT -> arrayOf(variants.indices.map { it + 1 }.toIntArray())
+        }
+        val column = when (layout) {
+            Layout.SQUARE, Layout.SQUARE_WITH_SKIN_TONE_CIRCLE -> template[0].size
+            Layout.FLAT -> minOf(6, template[0].size)
+        }
+        val row = when (layout) {
+            Layout.SQUARE, Layout.SQUARE_WITH_SKIN_TONE_CIRCLE -> template.size
+            Layout.FLAT -> variants.size / column + if (variants.size % column == 0) 0 else 1
+        }
+
+        // Rewrite template when the number of row mismatch
+        if (row != template.size) {
+            val overrideTemplate = Array(row) { IntArray(column) }
+            var index = 0
+            for (i in 0 until row) {
+                for (j in 0 until column) {
+                    if (index < template[0].size) {
+                        overrideTemplate[i][j] = template[0][index]
+                        index++
+                    }
+                }
+            }
+            template = overrideTemplate
+        }
+        return LayoutTemplate(template, row, column)
+    }
+
+    private data class LayoutTemplate(
+        val template: Array<IntArray>,
+        val numberOfRows: Int,
+        val numberOfColumns: Int
+    )
+
+    companion object {
+        private enum class Layout { FLAT, SQUARE, SQUARE_WITH_SKIN_TONE_CIRCLE }
+
+        /**
+         * The number of variants expected when using a square layout strategy. Square layouts are
+         * comprised of a 5x5 grid + the base variant.
+         */
+        private const val SQUARE_LAYOUT_VARIANT_COUNT = 26
+
+        // Set of emojis that use the square layout without skin tone swatches.
+        private val SQUARE_LAYOUT_EMOJI_NO_SKIN_TONE = setOf("👪")
+
+        private val SKIN_TONE_COLOR_RES_IDS = listOf(
+            R.color.light_skin_tone,
+            R.color.medium_light_skin_tone,
+            R.color.medium_skin_tone,
+            R.color.medium_dark_skin_tone,
+            R.color.dark_skin_tone
+        )
+
+        /**
+         * Square variant layout template with skin tone.
+         * 0 : a place holder
+         * -5: light skin tone circle
+         * -4: medium-light skin tone circle
+         * -3: medium skin tone circle
+         * -2: medium-dark skin tone circle
+         * -1: dark skin tone circle
+         * Positive number is the index + 1 in the variant array
+         */
+        private val SQUARE_LAYOUT_WITH_SKIN_TONES_TEMPLATE = arrayOf(
+            intArrayOf(0, 0, -5, -4, -3, -2, -1),
+            intArrayOf(0, -5, 2, 3, 4, 5, 6),
+            intArrayOf(0, -4, 7, 8, 9, 10, 11),
+            intArrayOf(0, -3, 12, 13, 14, 15, 16),
+            intArrayOf(0, -2, 17, 18, 19, 20, 21),
+            intArrayOf(1, -1, 22, 23, 24, 25, 26)
+        )
+
+        /**
+         * Square variant layout template without skin tone.
+         * 0 : a place holder
+         * Positive number is the index + 1 in the variant array
+         */
+        private val SQUARE_LAYOUT_TEMPLATE = arrayOf(
+            intArrayOf(0, 2, 3, 4, 5, 6),
+            intArrayOf(0, 7, 8, 9, 10, 11),
+            intArrayOf(0, 12, 13, 14, 15, 16),
+            intArrayOf(0, 17, 18, 19, 20, 21),
+            intArrayOf(1, 22, 23, 24, 25, 26)
+        )
+    }
+}
+
+internal class SkinToneCircleView @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null
+) : View(context, attrs) {
+    private val radius = resources.getDimension(R.dimen.emoji_picker_skin_tone_circle_radius)
+    var paint: Paint? = null
+
+    override fun draw(canvas: Canvas) {
+        super.draw(canvas)
+        canvas.apply {
+            paint?.let { drawCircle(width / 2f, height / 2f, radius, it) }
+        }
+    }
+}
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerPopupViewController.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerPopupViewController.kt
new file mode 100644
index 0000000..f7f3a1f
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerPopupViewController.kt
@@ -0,0 +1,82 @@
+package androidx.emoji2.emojipicker
+/*
+ * 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.
+ */
+
+import android.content.Context
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup.LayoutParams
+import android.view.WindowManager
+import android.widget.PopupWindow
+import android.widget.Toast
+import kotlin.math.roundToInt
+
+/**
+ * Default controller class for emoji picker popup view.
+ *
+ * <p>Shows the popup view above the target Emoji. View under control is a {@code
+ * EmojiPickerPopupView}.
+ */
+internal class EmojiPickerPopupViewController(
+    private val context: Context,
+    private val emojiPickerPopupView: EmojiPickerPopupView,
+    private val clickedEmojiView: View
+) {
+    private val popupWindow: PopupWindow = PopupWindow(
+        emojiPickerPopupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
+        /* focusable= */ false)
+
+    fun show() {
+        popupWindow.apply {
+            val location = IntArray(2)
+            clickedEmojiView.getLocationInWindow(location)
+            // Make the popup view center align with the target emoji view.
+            val x =
+                location[0] + clickedEmojiView.width / 2f -
+                    emojiPickerPopupView.getPopupViewWidth() / 2f
+            val y =
+                location[1] - emojiPickerPopupView.getPopupViewHeight()
+            // Set background drawable so that the popup window is dismissed properly when clicking
+            // outside / scrolling for API < 23.
+            setBackgroundDrawable(context.getDrawable(R.drawable.popup_view_rounded_background))
+            isOutsideTouchable = true
+            isTouchable = true
+            animationStyle = R.style.VariantPopupAnimation
+            elevation =
+                clickedEmojiView.context.resources
+                    .getDimensionPixelSize(R.dimen.emoji_picker_popup_view_elevation)
+                    .toFloat()
+            try {
+                showAtLocation(
+                    clickedEmojiView,
+                    Gravity.NO_GRAVITY,
+                    x.roundToInt(),
+                    y
+                )
+            } catch (e: WindowManager.BadTokenException) {
+                Toast.makeText(
+                    context, "Don't use EmojiPickerView inside a Popup",
+                    Toast.LENGTH_LONG).show()
+            }
+        }
+    }
+
+    fun dismiss() {
+        if (popupWindow.isShowing) {
+            popupWindow.dismiss()
+        }
+    }
+}
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
index 431a080..c6cdbf8 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
@@ -17,48 +17,23 @@
 package androidx.emoji2.emojipicker
 
 import android.content.Context
-import android.view.Gravity
-import android.view.LayoutInflater
+import android.view.View
 import android.view.View.OnLongClickListener
 import android.view.ViewGroup.LayoutParams
-import android.view.WindowManager
 import android.view.accessibility.AccessibilityEvent
-import android.widget.GridLayout
-import android.widget.PopupWindow
-import android.widget.Toast
 import androidx.recyclerview.widget.RecyclerView.ViewHolder
-import kotlin.math.roundToInt
 
 /** A [ViewHolder] containing an emoji view and emoji data.  */
 internal class EmojiViewHolder(
     context: Context,
     width: Int,
     height: Int,
-    private val layoutInflater: LayoutInflater,
     private val stickyVariantProvider: StickyVariantProvider,
     private val onEmojiPickedListener: EmojiViewHolder.(EmojiViewItem) -> Unit,
     private val onEmojiPickedFromPopupListener: EmojiViewHolder.(String) -> Unit
 ) : ViewHolder(EmojiView(context)) {
     private val onEmojiLongClickListener: OnLongClickListener = OnLongClickListener {
-        showPopupWindow(context) {
-            PopupViewHelper(context).fillPopupView(
-                it,
-                emojiView.measuredWidth,
-                emojiView.measuredHeight,
-                emojiViewItem.variants,
-                clickListener = { view ->
-                    val emojiPickedInPopup = (view as EmojiView).emoji.toString()
-                    onEmojiPickedFromPopupListener(emojiPickedInPopup)
-                    onEmojiPickedListener(makeEmojiViewItem(emojiPickedInPopup))
-                    // variants[0] is always the base (i.e., primary) emoji
-                    stickyVariantProvider.update(emojiViewItem.variants[0], emojiPickedInPopup)
-                    dismiss()
-                    // Hover on the base emoji after popup dismissed
-                    emojiView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
-                }
-            )
-        }
-        true
+        targetEmojiView -> showEmojiPopup(context, targetEmojiView)
     }
 
     private val emojiView: EmojiView = (itemView as EmojiView).apply {
@@ -70,7 +45,7 @@
         }
     }
     private lateinit var emojiViewItem: EmojiViewItem
-
+    private lateinit var emojiPickerPopupViewController: EmojiPickerPopupViewController
     fun bindEmoji(
         emoji: String,
     ) {
@@ -86,45 +61,23 @@
         }
     }
 
-    private fun showPopupWindow(
-        context: Context,
-        init: PopupWindow.(GridLayout) -> Unit
-    ) {
-        val popupView = layoutInflater
-            .inflate(R.layout.variant_popup, null, false)
-            .findViewById<GridLayout>(R.id.variant_popup)
-        PopupWindow(popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, false).apply {
-            init(popupView)
-            val location = IntArray(2)
-            emojiView.getLocationInWindow(location)
-            // Make the popup view center align with the target emoji view.
-            val x =
-                location[0] + emojiView.width / 2f - popupView.columnCount * emojiView.width / 2f
-            val y =
-                location[1] - popupView.rowCount * emojiView.height -
-                    popupView.paddingBottom - popupView.paddingTop
-            // Set background drawable so that the popup window is dismissed properly when clicking
-            // outside / scrolling for API < 23.
-            setBackgroundDrawable(context.getDrawable(R.drawable.popup_view_rounded_background))
-            isOutsideTouchable = true
-            isTouchable = true
-            animationStyle = R.style.VariantPopupAnimation
-            elevation =
-                emojiView.context.resources
-                    .getDimensionPixelSize(R.dimen.emoji_picker_popup_view_elevation)
-                    .toFloat()
-            try {
-                showAtLocation(
-                    emojiView,
-                    Gravity.NO_GRAVITY,
-                    x.roundToInt(),
-                    y
-                )
-            } catch (e: WindowManager.BadTokenException) {
-                Toast.makeText(
-                    context, "Don't use EmojiPickerView inside a Popup", Toast.LENGTH_LONG).show()
-            }
-        }
+    private fun showEmojiPopup(context: Context, clickedEmojiView: View): Boolean {
+        val emojiPickerPopupView = EmojiPickerPopupView(
+            context, /* attrs= */null, targetEmojiView = clickedEmojiView,
+            variants = emojiViewItem.variants, emojiViewOnClickListener = { view ->
+                val emojiPickedInPopup = (view as EmojiView).emoji.toString()
+                onEmojiPickedFromPopupListener(emojiPickedInPopup)
+                onEmojiPickedListener(makeEmojiViewItem(emojiPickedInPopup))
+                // variants[0] is always the base (i.e., primary) emoji
+                stickyVariantProvider.update(emojiViewItem.variants[0], emojiPickedInPopup)
+                emojiPickerPopupViewController.dismiss()
+                // Hover on the base emoji after popup dismissed
+                clickedEmojiView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
+            })
+        emojiPickerPopupViewController = EmojiPickerPopupViewController(
+            context, emojiPickerPopupView, clickedEmojiView)
+        emojiPickerPopupViewController.show()
+        return true
     }
 
     private fun makeEmojiViewItem(emoji: String) =
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/PopupViewHelper.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/PopupViewHelper.kt
deleted file mode 100644
index 2ba24b8..0000000
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/PopupViewHelper.kt
+++ /dev/null
@@ -1,168 +0,0 @@
-package androidx.emoji2.emojipicker
-
-import android.content.Context
-import android.graphics.Canvas
-import android.graphics.Paint
-import android.util.AttributeSet
-import android.view.View
-import android.view.View.OnClickListener
-import android.view.accessibility.AccessibilityEvent
-import android.widget.GridLayout
-import androidx.core.content.ContextCompat
-
-/*
- * 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.
- */
-
-internal class PopupViewHelper(private val context: Context) {
-    companion object {
-        private enum class Layout { FLAT, SQUARE, SQUARE_WITH_SKIN_TONE_CIRCLE }
-
-        private const val SQUARE_LAYOUT_VARIANT_COUNT = 26
-
-        // Set of emojis that use the square layout without skin tone.
-        private val SQUARE_LAYOUT_EMOJI_NO_SKIN_TONE = setOf("👪")
-
-        private val SKIN_TONE_COLOR_RES_IDS = listOf(
-            R.color.light_skin_tone,
-            R.color.medium_light_skin_tone,
-            R.color.medium_skin_tone,
-            R.color.medium_dark_skin_tone,
-            R.color.dark_skin_tone
-        )
-
-        /**
-         * Square variant layout template with skin tone.
-         * 0 : a place holder
-         * -5: light skin tone circle
-         * -4: medium-light skin tone circle
-         * -3: medium skin tone circle
-         * -2: medium-dark skin tone circle
-         * -1: dark skin tone circle
-         * Positive number is the index + 1 in the variant array
-         */
-        private val SQUARE_LAYOUT_WITH_SKIN_TONES_TEMPLATE = arrayOf(
-            intArrayOf(0, 0, -5, -4, -3, -2, -1),
-            intArrayOf(0, -5, 2, 3, 4, 5, 6),
-            intArrayOf(0, -4, 7, 8, 9, 10, 11),
-            intArrayOf(0, -3, 12, 13, 14, 15, 16),
-            intArrayOf(0, -2, 17, 18, 19, 20, 21),
-            intArrayOf(1, -1, 22, 23, 24, 25, 26)
-        )
-
-        /**
-         * Square variant layout template without skin tone.
-         * 0 : a place holder
-         * Positive number is the index + 1 in the variant array
-         */
-        private val SQUARE_LAYOUT_TEMPLATE = arrayOf(
-            intArrayOf(0, 2, 3, 4, 5, 6),
-            intArrayOf(0, 7, 8, 9, 10, 11),
-            intArrayOf(0, 12, 13, 14, 15, 16),
-            intArrayOf(0, 17, 18, 19, 20, 21),
-            intArrayOf(1, 22, 23, 24, 25, 26)
-        )
-    }
-
-    fun fillPopupView(
-        popupView: GridLayout,
-        gridWidth: Int,
-        gridHeight: Int,
-        variants: List<String>,
-        clickListener: OnClickListener
-    ): GridLayout {
-        val gridTemplate = getGridTemplate(variants)
-        popupView
-            .apply {
-                columnCount = gridTemplate.column
-                rowCount = gridTemplate.row
-                orientation = GridLayout.HORIZONTAL
-            }
-        gridTemplate.template.flatMap { it.asIterable() }.forEach {
-            val gridCell = when (it) {
-                in 1..variants.size ->
-                    EmojiView(context).apply {
-                        willDrawVariantIndicator = false
-                        emoji = variants[it - 1]
-                        setOnClickListener(clickListener)
-                        if (it == 1) {
-                            // Hover on the first emoji in the popup
-                            popupView.post {
-                                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
-                            }
-                        }
-                    }
-
-                0 -> EmojiView(context)
-
-                else -> SkinToneCircleView(context).apply {
-                    paint = Paint().apply {
-                        color = ContextCompat.getColor(context, SKIN_TONE_COLOR_RES_IDS[it + 5])
-                        style = Paint.Style.FILL
-                    }
-                }
-            }
-            popupView.addView(gridCell)
-            gridCell.layoutParams.width = gridWidth
-            gridCell.layoutParams.height = gridHeight
-        }
-        return popupView
-    }
-
-    private fun getGridTemplate(variants: List<String>): GridTemplate {
-        val layout =
-            if (variants.size == SQUARE_LAYOUT_VARIANT_COUNT)
-                if (SQUARE_LAYOUT_EMOJI_NO_SKIN_TONE.contains(variants[0]))
-                    Layout.SQUARE
-                else Layout.SQUARE_WITH_SKIN_TONE_CIRCLE
-            else Layout.FLAT
-        val template = when (layout) {
-            Layout.SQUARE -> SQUARE_LAYOUT_TEMPLATE
-            Layout.SQUARE_WITH_SKIN_TONE_CIRCLE -> SQUARE_LAYOUT_WITH_SKIN_TONES_TEMPLATE
-            Layout.FLAT -> arrayOf(variants.indices.map { it + 1 }.toIntArray())
-        }
-        val column = when (layout) {
-            Layout.SQUARE, Layout.SQUARE_WITH_SKIN_TONE_CIRCLE -> template[0].size
-            Layout.FLAT -> minOf(6, template[0].size)
-        }
-        val row = when (layout) {
-            Layout.SQUARE, Layout.SQUARE_WITH_SKIN_TONE_CIRCLE -> template.size
-            Layout.FLAT -> variants.size / column + if (variants.size % column == 0) 0 else 1
-        }
-
-        return GridTemplate(template, row, column)
-    }
-
-    private data class GridTemplate(
-        val template: Array<IntArray>,
-        val row: Int,
-        val column: Int
-    )
-}
-
-internal class SkinToneCircleView @JvmOverloads constructor(
-    context: Context,
-    attrs: AttributeSet? = null
-) : View(context, attrs) {
-    private val radius = resources.getDimension(R.dimen.emoji_picker_skin_tone_circle_radius)
-    var paint: Paint? = null
-
-    override fun draw(canvas: Canvas) {
-        super.draw(canvas)
-        canvas.apply {
-            paint?.let { drawCircle(width / 2f, height / 2f, radius, it) }
-        }
-    }
-}
diff --git a/emoji2/emoji2-emojipicker/src/main/res/layout/variant_popup.xml b/emoji2/emoji2-emojipicker/src/main/res/layout/variant_popup.xml
index 4c584f9..3a90b18 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/layout/variant_popup.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/layout/variant_popup.xml
@@ -14,13 +14,14 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<GridLayout
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/variant_popup"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
     android:background="@drawable/popup_view_rounded_background"
+    android:orientation="vertical"
     android:padding="@dimen/emoji_picker_popup_view_holder_padding_vertical"
     android:paddingStart="@dimen/emoji_picker_popup_view_holder_padding_start"
     android:paddingEnd="@dimen/emoji_picker_popup_view_holder_padding_end">
-</GridLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/fragment/fragment-compose/api/current.txt b/fragment/fragment-compose/api/current.txt
index e6f50d0..6c501a4 100644
--- a/fragment/fragment-compose/api/current.txt
+++ b/fragment/fragment-compose/api/current.txt
@@ -1 +1,9 @@
 // Signature format: 4.0
+package androidx.fragment.compose {
+
+  public final class FragmentKt {
+    method public static androidx.compose.ui.platform.ComposeView content(androidx.fragment.app.Fragment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+}
+
diff --git a/fragment/fragment-compose/api/restricted_current.txt b/fragment/fragment-compose/api/restricted_current.txt
index e6f50d0..6c501a4 100644
--- a/fragment/fragment-compose/api/restricted_current.txt
+++ b/fragment/fragment-compose/api/restricted_current.txt
@@ -1 +1,9 @@
 // Signature format: 4.0
+package androidx.fragment.compose {
+
+  public final class FragmentKt {
+    method public static androidx.compose.ui.platform.ComposeView content(androidx.fragment.app.Fragment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+}
+
diff --git a/fragment/fragment-compose/build.gradle b/fragment/fragment-compose/build.gradle
index cb5cf0d..9599d13 100644
--- a/fragment/fragment-compose/build.gradle
+++ b/fragment/fragment-compose/build.gradle
@@ -21,37 +21,32 @@
  * Please use that script when creating a new project, rather than copying an existing project and
  * modifying its settings.
  */
-import androidx.build.LibraryType
-import androidx.build.PlatformIdentifier
 import androidx.build.Publish
+import androidx.build.RunApiTasks
+import androidx.build.LibraryType
+
 
 plugins {
     id("AndroidXPlugin")
     id("AndroidXComposePlugin")
     id("com.android.library")
+    id("org.jetbrains.kotlin.android")
 }
 
-androidXMultiplatform {
-    android()
+dependencies {
 
-    defaultPlatform(PlatformIdentifier.ANDROID)
+    implementation(libs.kotlinStdlib)
+    api(project(":fragment:fragment-ktx"))
+    api("androidx.compose.runtime:runtime:1.5.4")
+    api("androidx.compose.ui:ui:1.5.4")
 
-    sourceSets {
-        androidMain {
-            dependencies {
-                api(project(":fragment:fragment-ktx"))
-            }
-        }
-
-        androidInstrumentedTest {
-            dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
-                implementation(libs.truth)
-            }
-        }
-    }
+    androidTestImplementation project(":compose:ui:ui-test-junit4")
+    androidTestImplementation project(":compose:material:material")
+    androidTestImplementation project(":compose:test-utils")
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.truth)
 }
 
 android {
diff --git a/fragment/fragment-compose/src/androidTest/AndroidManifest.xml b/fragment/fragment-compose/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..1c038a4
--- /dev/null
+++ b/fragment/fragment-compose/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <application>
+    <activity android:name="androidx.fragment.compose.test.TestActivity" />
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/fragment/fragment-compose/src/androidTest/java/androidx/fragment/compose/FragmentTest.kt b/fragment/fragment-compose/src/androidTest/java/androidx/fragment/compose/FragmentTest.kt
new file mode 100644
index 0000000..a9d39ff
--- /dev/null
+++ b/fragment/fragment-compose/src/androidTest/java/androidx/fragment/compose/FragmentTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.fragment.compose
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.compose.material.Text
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.commitNow
+import androidx.fragment.compose.test.TestActivity
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class FragmentTest {
+    @get:Rule
+    val testRule = createAndroidComposeRule<TestActivity>()
+
+    @Test
+    fun showContent() {
+        val fm = testRule.activity.supportFragmentManager
+        testRule.runOnUiThread {
+            fm.commitNow {
+                add(androidx.fragment.compose.test.R.id.fragment_container, MyFragment())
+            }
+        }
+
+        testRule.waitForIdle()
+
+        testRule.onNodeWithText("MyFragment").assertIsDisplayed()
+    }
+}
+
+class MyFragment : Fragment() {
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ) = content {
+        Text("MyFragment")
+    }
+}
diff --git a/core/core/src/main/java/androidx/core/util/Supplier.java b/fragment/fragment-compose/src/androidTest/java/androidx/fragment/compose/test/TestActivity.kt
similarity index 64%
copy from core/core/src/main/java/androidx/core/util/Supplier.java
copy to fragment/fragment-compose/src/androidTest/java/androidx/fragment/compose/test/TestActivity.kt
index 9deba96..ef92ecf 100644
--- a/core/core/src/main/java/androidx/core/util/Supplier.java
+++ b/fragment/fragment-compose/src/androidTest/java/androidx/fragment/compose/test/TestActivity.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,8 @@
  * limitations under the License.
  */
 
-package androidx.core.util;
+package androidx.fragment.compose.test
 
-/**
- * Compat version of {@link java.util.function.Supplier}
- * @param <T> the type of the input to the operation
- */
-public interface Supplier<T> {
+import androidx.fragment.app.FragmentActivity
 
-    /**
-     * Gets a result.
-     *
-     * @return a result
-     */
-    T get();
-}
+class TestActivity : FragmentActivity(R.layout.activity_main)
diff --git a/fragment/fragment-compose/src/androidTest/res/layout/activity_main.xml b/fragment/fragment-compose/src/androidTest/res/layout/activity_main.xml
new file mode 100644
index 0000000..e22d691
--- /dev/null
+++ b/fragment/fragment-compose/src/androidTest/res/layout/activity_main.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/fragment_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+</androidx.fragment.app.FragmentContainerView>
\ No newline at end of file
diff --git a/fragment/fragment-compose/src/main/java/androidx/fragment/compose/Fragment.kt b/fragment/fragment-compose/src/main/java/androidx/fragment/compose/Fragment.kt
new file mode 100644
index 0000000..ccdd0a9
--- /dev/null
+++ b/fragment/fragment-compose/src/main/java/androidx/fragment/compose/Fragment.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.fragment.compose
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.fragment.app.Fragment
+
+/**
+ * Wrapper function that handles the setup for creating a custom Fragment that hosts Compose
+ * content. It automatically sets the [ViewCompositionStrategy] to
+ * [ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed].
+ *
+ * It should be used as part of the implementation of [Fragment.onCreateView] and requires a context
+ * meaning the fragment must be attached to a FragmentManager.
+ *
+ * ```
+ * class ExampleFragment : Fragment() {
+ *     override fun onCreateView(
+ *         inflater: LayoutInflater,
+ *         container: ViewGroup?,
+ *         savedInstanceState: Bundle?
+ *     ) = content {
+ *         val viewModel: ExampleViewModel = viewModel()
+ *         // put your @Composable content here
+ *     }
+ * }
+ * ```
+ */
+fun Fragment.content(content: @Composable () -> Unit): ComposeView {
+    return ComposeView(requireContext()).apply {
+        setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+        setContent(content)
+    }
+}
diff --git a/fragment/fragment/api/current.txt b/fragment/fragment/api/current.txt
index 837aa19..7a1dab6 100644
--- a/fragment/fragment/api/current.txt
+++ b/fragment/fragment/api/current.txt
@@ -341,7 +341,9 @@
   }
 
   public static interface FragmentManager.OnBackStackChangedListener {
+    method @MainThread public default void onBackStackChangeCancelled();
     method @MainThread public default void onBackStackChangeCommitted(androidx.fragment.app.Fragment, boolean);
+    method @MainThread public default void onBackStackChangeProgressed(androidx.activity.BackEventCompat);
     method @MainThread public default void onBackStackChangeStarted(androidx.fragment.app.Fragment, boolean);
     method @MainThread public void onBackStackChanged();
   }
diff --git a/fragment/fragment/api/restricted_current.txt b/fragment/fragment/api/restricted_current.txt
index 8ad8ba1..880c2f1 100644
--- a/fragment/fragment/api/restricted_current.txt
+++ b/fragment/fragment/api/restricted_current.txt
@@ -346,7 +346,9 @@
   }
 
   public static interface FragmentManager.OnBackStackChangedListener {
+    method @MainThread public default void onBackStackChangeCancelled();
     method @MainThread public default void onBackStackChangeCommitted(androidx.fragment.app.Fragment, boolean);
+    method @MainThread public default void onBackStackChangeProgressed(androidx.activity.BackEventCompat);
     method @MainThread public default void onBackStackChangeStarted(androidx.fragment.app.Fragment, boolean);
     method @MainThread public void onBackStackChanged();
   }
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index 352bb4c..25050759 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -43,7 +43,7 @@
     api("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1")
     implementation("androidx.profileinstaller:profileinstaller:1.3.0")
     api("androidx.savedstate:savedstate:1.2.1")
-    api(projectOrArtifact(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
     api(libs.kotlinStdlib)
 
     androidTestImplementation("androidx.appcompat:appcompat:1.1.0", {
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 8a743100..743098b 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
@@ -461,6 +461,7 @@
             val fragment2 = StrictFragment()
             var startedCount = 0
             var committedCount = 0
+            var progress = 0f
 
             withActivity {
                 fragmentManager.beginTransaction()
@@ -487,6 +488,10 @@
                     startedCount++
                 }
 
+                override fun onBackStackChangeProgressed(backEventCompat: BackEventCompat) {
+                    progress = backEventCompat.progress
+                }
+
                 override fun onBackStackChangeCommitted(fragment: Fragment, pop: Boolean) {
                     committedCount++
                 }
@@ -498,8 +503,14 @@
                 executePendingTransactions()
             }
 
+            withActivity {
+                onBackPressedDispatcher.dispatchOnBackProgressed(BackEventCompat(0f, 0f, 0.5f, 0))
+                executePendingTransactions()
+            }
+
             if (FragmentManager.USE_PREDICTIVE_BACK) {
                 assertThat(startedCount).isEqualTo(1)
+                assertThat(progress).isEqualTo(0.5f)
             } else {
                 assertThat(startedCount).isEqualTo(0)
             }
@@ -527,6 +538,7 @@
             val fragment2 = StrictFragment()
             var startedCount = 0
             var committedCount = 0
+            var cancelledCount = 0
 
             withActivity {
                 fragmentManager.beginTransaction()
@@ -556,6 +568,10 @@
                 override fun onBackStackChangeCommitted(fragment: Fragment, pop: Boolean) {
                     committedCount++
                 }
+
+                override fun onBackStackChangeCancelled() {
+                    cancelledCount++
+                }
             }
             fragmentManager.addOnBackStackChangedListener(listener)
 
@@ -577,6 +593,7 @@
 
             if (FragmentManager.USE_PREDICTIVE_BACK) {
                 assertThat(startedCount).isEqualTo(1)
+                assertThat(cancelledCount).isEqualTo(1)
             } else {
                 assertThat(startedCount).isEqualTo(0)
             }
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 e208bca..f9569ce 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -250,6 +250,14 @@
         default void onBackStackChangeStarted(@NonNull Fragment fragment, boolean pop) { }
 
         /**
+         * Called whenever a predictive back gesture is changing the back stack.
+         *
+         * @param backEventCompat event that holds the current back gesture data
+         */
+        @MainThread
+        default void onBackStackChangeProgressed(@NonNull BackEventCompat backEventCompat) { }
+
+        /**
          * Called whenever the contents of a back stack change is committed.
          *
          * @param fragment that is affected by the committed back stack change
@@ -257,6 +265,12 @@
          */
         @MainThread
         default void onBackStackChangeCommitted(@NonNull Fragment fragment, boolean pop) { }
+
+        /**
+         * Called whenever a predictive back gesture is cancelled.
+         */
+        @MainThread
+        default void onBackStackChangeCancelled() { }
     }
 
     /**
@@ -497,6 +511,9 @@
                         for (SpecialEffectsController controller: changedControllers) {
                             controller.processProgress(backEvent);
                         }
+                        for (OnBackStackChangedListener listener : mBackStackChangeListeners) {
+                            listener.onBackStackChangeProgressed(backEvent);
+                        }
                     }
                 }
 
@@ -987,6 +1004,9 @@
             mTransitioningOp.mCommitted = false;
             mTransitioningOp.commit();
             executePendingTransactions();
+            for (OnBackStackChangedListener listener : mBackStackChangeListeners) {
+                listener.onBackStackChangeCancelled();
+            }
         }
     }
 
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultColorsAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultColorsAppWidget.kt
index 4a49342..e55f51f 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultColorsAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultColorsAppWidget.kt
@@ -76,7 +76,7 @@
             Column(
                 GlanceModifier
                     .padding(8.dp)
-                    .background(GlanceTheme.colors.background)
+                    .background(GlanceTheme.colors.widgetBackground)
             ) {
                 Button(
                     text = "Theme: $currentScheme",
diff --git a/glance/glance-appwidget/src/main/res/values-af/strings.xml b/glance/glance-appwidget/src/main/res/values-af/strings.xml
index 8db967b..1e3098b 100644
--- a/glance/glance-appwidget/src/main/res/values-af/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-af/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance-applegstukfout"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Gaan die presiese fout na deur "<b><tt>"adb logcat"</tt></b>" te gebruik en te soek na "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-am/strings.xml b/glance/glance-appwidget/src/main/res/values-am/strings.xml
index 304c144..efd1598 100644
--- a/glance/glance-appwidget/src/main/res/values-am/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-am/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"የGlance መተግበሪያ ምግብር ስህተት"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"GlanceAppWidet"</tt></b>" እየፈለጉ ሳለ "<b><tt>"adb logcat "</tt></b>" በመጠቀም ትክክለኛውን ስህተት ያጣሩ"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"ይዘትን ማሳየት አልተቻለም"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ar/strings.xml b/glance/glance-appwidget/src/main/res/values-ar/strings.xml
index 19f7f44..a4a0d45 100644
--- a/glance/glance-appwidget/src/main/res/values-ar/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ar/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531">"‏"<b>"خطأ في التطبيق المصغّر Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"‏تأكَّد من الخطأ بالضبط باستخدام "<b><tt>"أداة adb logcat"</tt></b>"، من خلال البحث عن "<b><tt>"GlanceAppWidget"</tt></b>"."</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-as/strings.xml b/glance/glance-appwidget/src/main/res/values-as/strings.xml
index 0ce6cddd..b54f48a 100644
--- a/glance/glance-appwidget/src/main/res/values-as/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-as/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance এপৰ ৱিজেটৰ আসোঁৱাহ"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"GlanceAppWidget"</tt></b>" বিচৰা সময়ত "<b><tt>"adb logcat"</tt></b>" ব্যৱহাৰ কৰি সঠিক আসোঁৱাহটো পৰীক্ষা কৰক"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"সমল দেখুৱাব নোৱাৰি"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-az/strings.xml b/glance/glance-appwidget/src/main/res/values-az/strings.xml
index 2508aa2..3244dd9 100644
--- a/glance/glance-appwidget/src/main/res/values-az/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-az/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance tətbiq vidceti xətası"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"Adb logcat"</tt></b>" istifadə edərək, "<b><tt>"GlanceAppWidget"</tt></b>" axtararaq dəqiq xətanı yoxlayın"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-b+sr+Latn/strings.xml b/glance/glance-appwidget/src/main/res/values-b+sr+Latn/strings.xml
index 474820d..2194879 100644
--- a/glance/glance-appwidget/src/main/res/values-b+sr+Latn/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-b+sr+Latn/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Greška u vezi sa vidžetom aplikacije Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Proverite u čemu je tačno greška pomoću stavke"<b><tt>"adb logcat"</tt></b>", tražeći stavku "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-be/strings.xml b/glance/glance-appwidget/src/main/res/values-be/strings.xml
index 416d581..6660049 100644
--- a/glance/glance-appwidget/src/main/res/values-be/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-be/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Памылка Glance App Widget"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Вызначыце дакладную памылку з дапамогай "<b><tt>"adb logcat"</tt></b>", увёўшы ў якасці пошукавага запыту "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Не ўдалося загрузіць змесціва"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-bg/strings.xml b/glance/glance-appwidget/src/main/res/values-bg/strings.xml
index ba3b6a8..c71e9d6 100644
--- a/glance/glance-appwidget/src/main/res/values-bg/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-bg/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Грешка в приспособлението за приложението Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Проверете точната грешка посредством "<b><tt>"adb logcat"</tt></b>", като потърсите "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-bn/strings.xml b/glance/glance-appwidget/src/main/res/values-bn/strings.xml
index c178749..4fc4bc9 100644
--- a/glance/glance-appwidget/src/main/res/values-bn/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-bn/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance App উইজেট সংক্রান্ত সমস্যা"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"আসল সমস্যাটি কী তা "<b><tt>"adb logcat"</tt></b>"-এর সাহায্যে চেক করুন, "<b><tt>"GlanceAppWidget"</tt></b>"-এর জন্য সার্চ করা হচ্ছে"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"কন্টেন্ট দেখানো যাচ্ছে না"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-bs/strings.xml b/glance/glance-appwidget/src/main/res/values-bs/strings.xml
index 9873eac..0975fca 100644
--- a/glance/glance-appwidget/src/main/res/values-bs/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-bs/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Greška vidžeta aplikacije Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Provjerite konkretnu grešku koristeći "<b><tt>"adb logcat"</tt></b>" i pretraživanjem "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Sadržaj se ne može prikazati"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ca/strings.xml b/glance/glance-appwidget/src/main/res/values-ca/strings.xml
index 3ac2b31..5b2cf5c 100644
--- a/glance/glance-appwidget/src/main/res/values-ca/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ca/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Error de Glance App Widget"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Consulta l\'error exacte fent servir "<b><tt>"adb logcat"</tt></b>" i cercant "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-cs/strings.xml b/glance/glance-appwidget/src/main/res/values-cs/strings.xml
index 5aea363..872b32b 100644
--- a/glance/glance-appwidget/src/main/res/values-cs/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-cs/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Chyba widgetu aplikace Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Pomocí "<b><tt>"adb logcat"</tt></b>" zkontrolujte přesné znění chyby: vyhledejte "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-da/strings.xml b/glance/glance-appwidget/src/main/res/values-da/strings.xml
index d86098e..b01ca60 100644
--- a/glance/glance-appwidget/src/main/res/values-da/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-da/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Fejl i appwidgetten Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Brug "<b><tt>"adb logcat"</tt></b>" til at tjekke den specifikke fejl ved at søge efter "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-de/strings.xml b/glance/glance-appwidget/src/main/res/values-de/strings.xml
index 9a97c49..bd6990e 100644
--- a/glance/glance-appwidget/src/main/res/values-de/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-de/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"„Glance App Widget“-Fehler"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Prüfe den genauen Fehler mithilfe von "<b><tt>"adb logcat"</tt></b>" und suche dabei nach "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Inhalt kann nicht angezeigt werden"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-el/strings.xml b/glance/glance-appwidget/src/main/res/values-el/strings.xml
index b627659..b148f7c 100644
--- a/glance/glance-appwidget/src/main/res/values-el/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-el/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Σφάλμα γραφικού στοιχείου εφαρμογής Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Ελέγξτε το ακριβές σφάλμα χρησιμοποιώντας το "<b><tt>"adb logcat"</tt></b>" και αναζητώντας το στοιχείο "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-en-rAU/strings.xml b/glance/glance-appwidget/src/main/res/values-en-rAU/strings.xml
index 6c19aec..e812d63 100644
--- a/glance/glance-appwidget/src/main/res/values-en-rAU/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-en-rAU/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance app widget error"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Check the exact error using "<b><tt>"adb Logcat"</tt></b>", searching for "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-en-rCA/strings.xml b/glance/glance-appwidget/src/main/res/values-en-rCA/strings.xml
index 463e089..10fc413 100644
--- a/glance/glance-appwidget/src/main/res/values-en-rCA/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-en-rCA/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance App Widget Error"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Check the exact error using "<b><tt>"adb logcat"</tt></b>", searching for "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Can\'t show content"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-en-rGB/strings.xml b/glance/glance-appwidget/src/main/res/values-en-rGB/strings.xml
index 6c19aec..e812d63 100644
--- a/glance/glance-appwidget/src/main/res/values-en-rGB/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-en-rGB/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance app widget error"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Check the exact error using "<b><tt>"adb Logcat"</tt></b>", searching for "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-en-rIN/strings.xml b/glance/glance-appwidget/src/main/res/values-en-rIN/strings.xml
index 6c19aec..e812d63 100644
--- a/glance/glance-appwidget/src/main/res/values-en-rIN/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-en-rIN/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance app widget error"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Check the exact error using "<b><tt>"adb Logcat"</tt></b>", searching for "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-en-rXC/strings.xml b/glance/glance-appwidget/src/main/res/values-en-rXC/strings.xml
index b8cf6b8..2c657e27 100644
--- a/glance/glance-appwidget/src/main/res/values-en-rXC/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-en-rXC/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎Glance App Widget Error‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎Check the exact error using ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"<tt>"‎‏‎‎‏‏‏‎adb logcat‎‏‎‎‏‏‎"</tt>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎, searching for ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"<tt>"‎‏‎‎‏‏‏‎GlanceAppWidget‎‏‎‎‏‏‎"</tt>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎Can\'t show content‎‏‎‎‏‎"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-es-rUS/strings.xml b/glance/glance-appwidget/src/main/res/values-es-rUS/strings.xml
index d718ec3..a4e2a34 100644
--- a/glance/glance-appwidget/src/main/res/values-es-rUS/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-es-rUS/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Error en widget de la app de Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Para ver el error exacto, usa "<b><tt>"adb logcat"</tt></b>" y busca "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"No se puede mostrar el contenido"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-es/strings.xml b/glance/glance-appwidget/src/main/res/values-es/strings.xml
index 9f52cf0..1791bc0 100644
--- a/glance/glance-appwidget/src/main/res/values-es/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-es/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Error del widget de la aplicación Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Revisa el error exacto mediante "<b><tt>"adb logcat"</tt></b>" y buscando "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-et/strings.xml b/glance/glance-appwidget/src/main/res/values-et/strings.xml
index 665dc58..3b9ad9a 100644
--- a/glance/glance-appwidget/src/main/res/values-et/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-et/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Rakenduse Ülevaade vidina viga"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Kontrollige konkreetset viga tööriistaga "<b><tt>"adb logcat"</tt></b>", sisestades otsingu "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-eu/strings.xml b/glance/glance-appwidget/src/main/res/values-eu/strings.xml
index 28c7e98..e67b27f 100644
--- a/glance/glance-appwidget/src/main/res/values-eu/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-eu/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance aplikazioaren widgetaren errorea"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Errore zehatza ikusteko, erabili "<b><tt>"adb logcat"</tt></b>" bertan "<b><tt>"GlanceAppWidget"</tt></b>" bilatzeko"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-fa/strings.xml b/glance/glance-appwidget/src/main/res/values-fa/strings.xml
index c462873..5dd49dc 100644
--- a/glance/glance-appwidget/src/main/res/values-fa/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-fa/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531">"‏"<b>"خطا در ابزارک برنامه Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"‏بااستفاده از "<b><tt>"adb logcat"</tt></b>" و جستجوی "<b><tt>"GlanceAppWidget"</tt></b>"، خطا را به‌طور دقیق بررسی کنید"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"محتوا نشان داده نشد"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-fi/strings.xml b/glance/glance-appwidget/src/main/res/values-fi/strings.xml
index 6ab1e71..09aa632 100644
--- a/glance/glance-appwidget/src/main/res/values-fi/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-fi/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Virhe Glance-sovelluswidgetissä"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Tarkista tarkka virhe "<b><tt>"adb logcat"</tt></b>" ‑komennolla. Tee sitä varten haku "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Sisältöä ei voi näyttää"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-fr-rCA/strings.xml b/glance/glance-appwidget/src/main/res/values-fr-rCA/strings.xml
index 726a961..7b97e4f 100644
--- a/glance/glance-appwidget/src/main/res/values-fr-rCA/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-fr-rCA/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Erreur provenant de la librairie Glance App Widget"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Vérifiez l\'erreur exacte en utilisant "<b><tt>"adb logcat"</tt></b>", en recherchant "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-fr/strings.xml b/glance/glance-appwidget/src/main/res/values-fr/strings.xml
index 2679e31..e3801a32 100644
--- a/glance/glance-appwidget/src/main/res/values-fr/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-fr/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Erreur du widget de l\'appli Aperçu"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Vous pouvez utiliser "<b><tt>"adb logcat"</tt></b>" pour connaître l\'erreur exacte lorsque vous recherchez "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Impossible d\'afficher le contenu"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-gl/strings.xml b/glance/glance-appwidget/src/main/res/values-gl/strings.xml
index d8ba4d6..c223cba 100644
--- a/glance/glance-appwidget/src/main/res/values-gl/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-gl/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Erro do widget da aplicación Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Comproba o erro exacto usando "<b><tt>"adb logcat"</tt></b>" e buscando "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-gu/strings.xml b/glance/glance-appwidget/src/main/res/values-gu/strings.xml
index bd5a3da..a378b09 100644
--- a/glance/glance-appwidget/src/main/res/values-gu/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-gu/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance ઍપ વિજેટમાં ભૂલ"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"adb logcat"</tt></b>"નો ઉપયોગ કરીને, "<b><tt>"GlanceAppWidget"</tt></b>" શોધીને ચોક્કસ ભૂલ ચેક કરો"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-hi/strings.xml b/glance/glance-appwidget/src/main/res/values-hi/strings.xml
index 5abc224..574e22b 100644
--- a/glance/glance-appwidget/src/main/res/values-hi/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-hi/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance App Widget की गड़बड़ी"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"adb logcat"</tt></b>" की मदद से, "<b><tt>"GlanceAppWidget"</tt></b>" को ढूंढकर असल गड़बड़ी का पता लगाएं"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-hr/strings.xml b/glance/glance-appwidget/src/main/res/values-hr/strings.xml
index 111f2bc..e4ca81d 100644
--- a/glance/glance-appwidget/src/main/res/values-hr/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-hr/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Pogreška widgeta aplikacije Kratki pregled"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Konkretnu pogrešku provjerite koristeći "<b><tt>"adb logcat"</tt></b>" i potražite "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Sadržaj se ne može prikazati"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-hu/strings.xml b/glance/glance-appwidget/src/main/res/values-hu/strings.xml
index aba3801..2778123 100644
--- a/glance/glance-appwidget/src/main/res/values-hu/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-hu/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Hiba a Glance alkalmazásmodulnál"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Ellenőrizze a pontos hibát az "<b><tt>"adb logcat"</tt></b>" paranccsal, a "<b><tt>"GlanceAppWidget"</tt></b>" modulra keresve"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"A tartalom nem jeleníthető meg"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-hy/strings.xml b/glance/glance-appwidget/src/main/res/values-hy/strings.xml
index 036c2d9..3409cd3 100644
--- a/glance/glance-appwidget/src/main/res/values-hy/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-hy/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"«Հակիրճ գլխավորի մասին» վիջեթի սխալ"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Ստուգեք, թե իրականում ինչ սխալ է տեղի ունեցել, "<b><tt>"adb logcat"</tt></b>"-ի միջոցով՝ որոնելով "<b><tt>"GlanceAppWidget"</tt></b>" բառը"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-in/strings.xml b/glance/glance-appwidget/src/main/res/values-in/strings.xml
index b8674c9..995c8f9 100644
--- a/glance/glance-appwidget/src/main/res/values-in/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-in/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Error library Widget Aplikasi Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Periksa error yang tepat menggunakan "<b><tt>"logcat adb"</tt></b>" saat menelusuri "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-is/strings.xml b/glance/glance-appwidget/src/main/res/values-is/strings.xml
index 9c39b98..af6d0fb 100644
--- a/glance/glance-appwidget/src/main/res/values-is/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-is/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Villa í forritsgræju „Í fljótu bragði“"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Skoðaðu villuna með "<b><tt>"adb logcat"</tt></b>" og leitaðu að "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-it/strings.xml b/glance/glance-appwidget/src/main/res/values-it/strings.xml
index 1f0c434..a169b36 100644
--- a/glance/glance-appwidget/src/main/res/values-it/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-it/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Errore del widget dell\'app Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Controlla l\'errore esatto con "<b><tt>"adb logcat"</tt></b>", cercando "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-iw/strings.xml b/glance/glance-appwidget/src/main/res/values-iw/strings.xml
index e8a9e0d..cbfc3b8 100644
--- a/glance/glance-appwidget/src/main/res/values-iw/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-iw/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"שגיאה בווידג\'ט של האפליקציה \'בקצרה\'"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"‏יש לבדוק את השגיאה המדויקת באמצעות "<b><tt>"adb Logcat‏"</tt></b>", ולחפש את "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ja/strings.xml b/glance/glance-appwidget/src/main/res/values-ja/strings.xml
index b75fedc..44b11ad 100644
--- a/glance/glance-appwidget/src/main/res/values-ja/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ja/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Google Pixel のスナップショット アプリ ウィジェット エラー"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"adb logcat"</tt></b>" を使用して "<b><tt>"GlanceAppWidget"</tt></b>" を検索し、エラーを詳しくご確認ください"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"コンテンツを表示できません"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ka/strings.xml b/glance/glance-appwidget/src/main/res/values-ka/strings.xml
index 71c5995..40a6d80 100644
--- a/glance/glance-appwidget/src/main/res/values-ka/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ka/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance აპის ვიჯეტის შეცდომა"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"შეამოწმეთ ზუსტი შეცდომა "<b><tt>"adb logcat"</tt></b>"-ის მეშვეობით, მოიძიეთ "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"კონტენტის ჩვენება ვერ ხერხდება"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-kk/strings.xml b/glance/glance-appwidget/src/main/res/values-kk/strings.xml
index 1619cee..8268784 100644
--- a/glance/glance-appwidget/src/main/res/values-kk/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-kk/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance App Widget қатесі"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Қатені дәл анықтау үшін "<b><tt>"adb logcat"</tt></b>" мәнін қолданыңыз және "<b><tt>"GlanceAppWidget"</tt></b>" іздеңіз."</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-km/strings.xml b/glance/glance-appwidget/src/main/res/values-km/strings.xml
index e95814e..167ba10 100644
--- a/glance/glance-appwidget/src/main/res/values-km/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-km/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"បញ្ហា​ពាក់ព័ន្ធនឹង​ធាតុក្រាហ្វិក​កម្មវិធី​មើលមួយភ្លែត"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"ពិនិត្យមើល​បញ្ហាជាក់លាក់​ដោយប្រើ "<b><tt>"adb logcat"</tt></b>", កំពុង​ស្វែងរក "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"មិនអាចបង្ហាញ​ខ្លឹមសារបានទេ"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-kn/strings.xml b/glance/glance-appwidget/src/main/res/values-kn/strings.xml
index 846ab94..f02fd27 100644
--- a/glance/glance-appwidget/src/main/res/values-kn/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-kn/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance ಆ್ಯಪ್ ವಿಜೆಟ್ ದೋಷ"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"adb logcat"</tt></b>" ಬಳಸಿಕೊಂಡು ನಿಖರವಾದ ದೋಷವನ್ನು ಪರಿಶೀಲಿಸಿ, "<b><tt>"GlanceAppWidget"</tt></b>" ಅನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ko/strings.xml b/glance/glance-appwidget/src/main/res/values-ko/strings.xml
index d24285f..527abf1 100644
--- a/glance/glance-appwidget/src/main/res/values-ko/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ko/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance 앱 위젯 오류"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"adb logcat"</tt></b>"을 사용하여 정확한 오류 확인, "<b><tt>"GlanceAppWidget"</tt></b>" 검색"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ky/strings.xml b/glance/glance-appwidget/src/main/res/values-ky/strings.xml
index 8761bbf..8946e1c 100644
--- a/glance/glance-appwidget/src/main/res/values-ky/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ky/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance колдонмосунун виджет катасы"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"GlanceAppWidget"</tt></b>" cурамы менен издеп, "<b><tt>"adb logcat"</tt></b>" аркылуу катаны аныктаңыз"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-lo/strings.xml b/glance/glance-appwidget/src/main/res/values-lo/strings.xml
index 8802d1e..df91475 100644
--- a/glance/glance-appwidget/src/main/res/values-lo/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-lo/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"ວິດເຈັດແອັບ Glance ຜິດພາດ"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"ກວດສອບຂໍ້ຜິດພາດທີ່ແນ່ນອນໂດຍໃຊ້ "<b><tt>"adb Logcat"</tt></b>", ກຳລັງຊອກຫາ "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"ບໍ່ສາມາດສະແດງເນື້ອຫາໄດ້"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-lt/strings.xml b/glance/glance-appwidget/src/main/res/values-lt/strings.xml
index 011b8c9..d2032a8 100644
--- a/glance/glance-appwidget/src/main/res/values-lt/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-lt/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Programos „Glance“ valdiklio klaida"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Patikrinkite tikslią klaidą naudodami "<b><tt>"ADB Logcat"</tt></b>", kai ieškote "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-lv/strings.xml b/glance/glance-appwidget/src/main/res/values-lv/strings.xml
index 764469a..a132297 100644
--- a/glance/glance-appwidget/src/main/res/values-lv/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-lv/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance lietotnes logrīka kļūda"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Atrodiet precīzu kļūdu ar rīka "<b><tt>"adb logcat"</tt></b>" palīdzību, izmantojot meklēšanas vaicājumu "<b><tt>"GlanceAppWidget"</tt></b>"."</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-mk/strings.xml b/glance/glance-appwidget/src/main/res/values-mk/strings.xml
index 216d054..da90e36 100644
--- a/glance/glance-appwidget/src/main/res/values-mk/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-mk/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Грешка на Glance App Widget"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Проверете ја точната грешка со "<b><tt>"adb logcat"</tt></b>" при пребарување "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ml/strings.xml b/glance/glance-appwidget/src/main/res/values-ml/strings.xml
index 83b80a6..31af002 100644
--- a/glance/glance-appwidget/src/main/res/values-ml/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ml/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance ആപ്പ് വിജറ്റ് പിശക്"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"adb logcat"</tt></b>" ഉപയോഗിച്ച് യഥാർത്ഥ പിശക് പരിശോധിക്കുക, "<b><tt>"GlanceAppWidget"</tt></b>" എന്നത് തിരയുന്നു"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"ഉള്ളടക്കം കാണിക്കാനാകില്ല"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-mn/strings.xml b/glance/glance-appwidget/src/main/res/values-mn/strings.xml
index 3c0adc2..8e40cac 100644
--- a/glance/glance-appwidget/src/main/res/values-mn/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-mn/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Гялс харах аппын виджетийн алдаа"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"Adb logcat"</tt></b>" ашиглан, "<b><tt>"GlanceAppWidget"</tt></b>"-г хайж тодорхой алдааг шалгана уу"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-mr/strings.xml b/glance/glance-appwidget/src/main/res/values-mr/strings.xml
index 9678594..9334dde 100644
--- a/glance/glance-appwidget/src/main/res/values-mr/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-mr/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance अ‍ॅप विजेट एरर"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"GlanceAppWidget"</tt></b>" शोधून, "<b><tt>"adb logcat"</tt></b>" वापरून नेमकी एरर तपासा"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ms/strings.xml b/glance/glance-appwidget/src/main/res/values-ms/strings.xml
index 2fe6701..c0ce21d 100644
--- a/glance/glance-appwidget/src/main/res/values-ms/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ms/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Ralat Widget Apl Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Semak ralat sebenar menggunakan "<b><tt>"adb logcat"</tt></b>" semasa mencari "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Tidak dapat memaparkan kandungan"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-my/strings.xml b/glance/glance-appwidget/src/main/res/values-my/strings.xml
index 1c0e468..662b0b3 100644
--- a/glance/glance-appwidget/src/main/res/values-my/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-my/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance အက်ပ်ဝိဂျက်အမှား"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"GlanceAppWidget"</tt></b>" ရှာဖွေရာတွင် "<b><tt>"adb logcat"</tt></b>" သုံးပြီး အမှားအတိအကျကို ရှာနိုင်သည်"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-nb/strings.xml b/glance/glance-appwidget/src/main/res/values-nb/strings.xml
index 3a79062..8a31582 100644
--- a/glance/glance-appwidget/src/main/res/values-nb/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-nb/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Modulfeil med Kort fortalt-appen"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Sjekk den nøyaktige feilen ved å bruke "<b><tt>"adb logcat"</tt></b>" og søke etter "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ne/strings.xml b/glance/glance-appwidget/src/main/res/values-ne/strings.xml
index 224c701..bf4dc05 100644
--- a/glance/glance-appwidget/src/main/res/values-ne/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ne/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance एपको विजेटसम्बन्धी त्रुटि"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"adb logcat"</tt></b>" प्रयोग गरी "<b><tt>"GlanceAppWidget"</tt></b>" खोजेर यथार्थ त्रुटि पत्ता लगाउनुहोस्"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-nl/strings.xml b/glance/glance-appwidget/src/main/res/values-nl/strings.xml
index 763e397..673993c 100644
--- a/glance/glance-appwidget/src/main/res/values-nl/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-nl/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Fout met Glance-app-widget"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Check de exacte fout via "<b><tt>"adb logcat"</tt></b>" door te zoeken naar "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Kan content niet tonen"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-or/strings.xml b/glance/glance-appwidget/src/main/res/values-or/strings.xml
index 65b9858..82a8829 100644
--- a/glance/glance-appwidget/src/main/res/values-or/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-or/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance ଆପ ୱିଜେଟରେ ତ୍ରୁଟି"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"adb logcat"</tt></b>" ବ୍ୟବହାର କରି, "<b><tt>"GlanceAppWidget"</tt></b>" ସନ୍ଧାନ କରି ପ୍ରକୃତ ତ୍ରୁଟି ଯାଞ୍ଚ କରନ୍ତୁ"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-pa/strings.xml b/glance/glance-appwidget/src/main/res/values-pa/strings.xml
index 6892ffb..f2d0ab9 100644
--- a/glance/glance-appwidget/src/main/res/values-pa/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-pa/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance ਐਪ ਵਿਜੇਟ ਸੰਬੰਧੀ ਗੜਬੜ"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"adb logcat"</tt>" ਦੀ ਵਰਤੋਂ ਨਾਲ "</b>", "<b><tt>"GlanceAppWidget"</tt></b>" ਦੀ ਖੋਜ ਕਰ ਕੇ ਸਟੀਕ ਗੜਬੜ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-pl/strings.xml b/glance/glance-appwidget/src/main/res/values-pl/strings.xml
index f537ca9..7482a96 100644
--- a/glance/glance-appwidget/src/main/res/values-pl/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-pl/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Błąd widżetu aplikacji W skrócie"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Sprawdź dokładny błąd w "<b><tt>"adb logcat"</tt></b>" (wyszukaj "<b><tt>"GlanceAppWidget"</tt></b>")"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-pt-rBR/strings.xml b/glance/glance-appwidget/src/main/res/values-pt-rBR/strings.xml
index 8ca5768..ff1064f 100644
--- a/glance/glance-appwidget/src/main/res/values-pt-rBR/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-pt-rBR/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Erro no widget do app Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Pesquise "<b><tt>"GlanceAppWidget"</tt></b>" no "<b><tt>"adb logcat"</tt></b>" para consultar o erro exato"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Não é possível mostrar conteúdo"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-pt-rPT/strings.xml b/glance/glance-appwidget/src/main/res/values-pt-rPT/strings.xml
index 6f5985a..b60578f 100644
--- a/glance/glance-appwidget/src/main/res/values-pt-rPT/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-pt-rPT/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Erro de widget da app Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Verifique o erro exato através de "<b><tt>"adb logcat"</tt></b>" ao pesquisar "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Não é possível mostrar o conteúdo"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-pt/strings.xml b/glance/glance-appwidget/src/main/res/values-pt/strings.xml
index 8ca5768..ff1064f 100644
--- a/glance/glance-appwidget/src/main/res/values-pt/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-pt/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Erro no widget do app Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Pesquise "<b><tt>"GlanceAppWidget"</tt></b>" no "<b><tt>"adb logcat"</tt></b>" para consultar o erro exato"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Não é possível mostrar conteúdo"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ro/strings.xml b/glance/glance-appwidget/src/main/res/values-ro/strings.xml
index 6ba073d..a7e1041 100644
--- a/glance/glance-appwidget/src/main/res/values-ro/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ro/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Eroare legată de widgetul aplicației Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Vezi eroarea exactă folosind "<b><tt>"adb logcat"</tt></b>" și căutând "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ru/strings.xml b/glance/glance-appwidget/src/main/res/values-ru/strings.xml
index 924521f..29efc59 100644
--- a/glance/glance-appwidget/src/main/res/values-ru/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ru/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Ошибка, связанная с виджетом приложения в Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Выявите ошибку с помощью "<b><tt>"adb logcat"</tt></b>". В этом инструменте выполните поиск по запросу "<b><tt>"GlanceAppWidget"</tt></b>"."</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-si/strings.xml b/glance/glance-appwidget/src/main/res/values-si/strings.xml
index 8a1377a..31bb754 100644
--- a/glance/glance-appwidget/src/main/res/values-si/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-si/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance යෙදුම් විජට්ටු දෝෂය"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"නිවැරදි දෝෂය "<b><tt>"GlanceAppWidget"</tt></b>" සොයන "<b><tt>"adb logcat"</tt></b>" භාවිත කර පරීක්ෂා කරන්න"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-sk/strings.xml b/glance/glance-appwidget/src/main/res/values-sk/strings.xml
index cfe8e2e..69e9573 100644
--- a/glance/glance-appwidget/src/main/res/values-sk/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-sk/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Chyba miniaplikácie Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Pomocou nástroja "<b><tt>"adb logcat"</tt></b>" zistite presnú príčinu chyby tak, že vyhľadáte parameter "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Obsah nie je možné zobraziť"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-sl/strings.xml b/glance/glance-appwidget/src/main/res/values-sl/strings.xml
index 376e740..f556022 100644
--- a/glance/glance-appwidget/src/main/res/values-sl/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-sl/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Napaka pripomočka za aplikacijo Hitri pregled"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Preverite točno napako tako, da z mehanizmom "<b><tt>"adb logcat"</tt></b>" poiščete "<b><tt>"GlanceAppWidget"</tt></b>"."</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Ni mogoče prikazati vsebine"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-sq/strings.xml b/glance/glance-appwidget/src/main/res/values-sq/strings.xml
index b33bbb9..3d04098 100644
--- a/glance/glance-appwidget/src/main/res/values-sq/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-sq/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Gabim i Glance App Widget"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Kontrollo gabimin ekzakt duke përdorur "<b><tt>"adb logcat"</tt></b>", duke kërkuar për "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-sr/strings.xml b/glance/glance-appwidget/src/main/res/values-sr/strings.xml
index c3e726d..3fbcc79 100644
--- a/glance/glance-appwidget/src/main/res/values-sr/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-sr/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Грешка у вези са виџетом апликације Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Проверите у чему је тачно грешка помоћу ставке"<b><tt>"adb logcat"</tt></b>", тражећи ставку "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-sv/strings.xml b/glance/glance-appwidget/src/main/res/values-sv/strings.xml
index b89910d..efcc2fb 100644
--- a/glance/glance-appwidget/src/main/res/values-sv/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-sv/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Fel i appwidget som skapats med Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Kontrollera exakt vad som är fel genom att söka efter "<b><tt>"GlanceAppWidget"</tt></b>" i utdata från "<b><tt>"adb logcat"</tt></b>"."</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-sw/strings.xml b/glance/glance-appwidget/src/main/res/values-sw/strings.xml
index 88d1821..85186f7 100644
--- a/glance/glance-appwidget/src/main/res/values-sw/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-sw/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Hitilafu ya Wijeti ya Programu ya Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Angalia hitilafu mahususi ukitumia "<b><tt>"adb logcat"</tt></b>", ukitafuta "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Imeshindwa kuonyesha maudhui"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ta/strings.xml b/glance/glance-appwidget/src/main/res/values-ta/strings.xml
index dac5671..35e91f5 100644
--- a/glance/glance-appwidget/src/main/res/values-ta/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ta/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance ஆப்ஸ் விட்ஜெட் பிழை"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"adb logcat"</tt></b>" என்பதைப் பயன்படுத்தி சரியான பிழையைக் கண்டறியவும், "<b><tt>"GlanceAppWidget"</tt></b>" தேடப்படுகிறது"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-te/strings.xml b/glance/glance-appwidget/src/main/res/values-te/strings.xml
index 41de5db6..4998679 100644
--- a/glance/glance-appwidget/src/main/res/values-te/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-te/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance యాప్ విడ్జెట్ ఎర్రర్"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033"><b><tt>"adb logcat"</tt></b>"‌ను ఉపయోగించి ఖచ్చితమైన ఎర్రర్‌ను చెక్ చేయండి, "<b><tt>"GlanceAppWidget"</tt></b>" కోసం సెర్చ్ చేస్తుంది"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"కంటెంట్‌ను చూపడం సాధ్యం కాదు"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-th/strings.xml b/glance/glance-appwidget/src/main/res/values-th/strings.xml
index 5fb6418..abd1c12 100644
--- a/glance/glance-appwidget/src/main/res/values-th/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-th/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"ข้อผิดพลาด Glance App Widget"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"ตรวจสอบข้อผิดพลาดตามที่ปรากฏโดยใช้ "<b><tt>"adb logcat"</tt></b>" เพื่อค้นหา "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"แสดงเนื้อหาไม่ได้"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-tl/strings.xml b/glance/glance-appwidget/src/main/res/values-tl/strings.xml
index 424b1f6..95d2331 100644
--- a/glance/glance-appwidget/src/main/res/values-tl/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-tl/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Error sa Widget ng Glance App"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Tingnan ang eksaktong error gamit ang "<b><tt>"adb logcat"</tt></b>", at paghahanap sa "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-tr/strings.xml b/glance/glance-appwidget/src/main/res/values-tr/strings.xml
index 43d5251..aca9fdc 100644
--- a/glance/glance-appwidget/src/main/res/values-tr/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-tr/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance App Widget Hatası"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Hatanın tam olarak ne olduğunu bulmak için "<b><tt>"adb logcat"</tt></b>"\'i kullanarak "<b><tt>"GlanceAppWidget"</tt></b>" araması yapın."</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"İçerik gösterilemiyor"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-uk/strings.xml b/glance/glance-appwidget/src/main/res/values-uk/strings.xml
index a763344..ecd6171 100644
--- a/glance/glance-appwidget/src/main/res/values-uk/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-uk/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Помилка віджета додатка Glance"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Щоб отримати точні відомості про помилку, скористайтеся командою "<b><tt>"adb logcat"</tt></b>" під час пошуку за запитом "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-ur/strings.xml b/glance/glance-appwidget/src/main/res/values-ur/strings.xml
index 00e7a24..0a4dc17 100644
--- a/glance/glance-appwidget/src/main/res/values-ur/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-ur/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531">"‏"<b>"Glance App Widget کی خرابی"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"‏"<b><tt>"GlanceAppWidget"</tt></b>" کو تلاش کرتے ہوئے "<b><tt>"adb logcat"</tt></b>" کا استعمال کر کے اصل خرابی کو چیک کریں"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"مواد نہیں دکھایا جاسکتا"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-uz/strings.xml b/glance/glance-appwidget/src/main/res/values-uz/strings.xml
index 0574a64..71690c5 100644
--- a/glance/glance-appwidget/src/main/res/values-uz/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-uz/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance ilova vidjeti xatosi"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Muayyan xatoni "<b><tt>"adb logcat"</tt></b>" yordamida tekshiring va "<b><tt>"GlanceAppWidget"</tt>" qatorini qidiring"</b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-vi/strings.xml b/glance/glance-appwidget/src/main/res/values-vi/strings.xml
index 1c1be47..db87a72 100644
--- a/glance/glance-appwidget/src/main/res/values-vi/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-vi/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Lỗi Glance App Widget (Tiện ích xem nhanh ứng dụng)"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Xem đúng lỗi bằng cách dùng "<b><tt>"logcat adb"</tt></b>" để tìm "<b><tt>"GlanceAppWidget"</tt></b></string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-zh-rCN/strings.xml b/glance/glance-appwidget/src/main/res/values-zh-rCN/strings.xml
index 7b09c4b..abb70fe 100644
--- a/glance/glance-appwidget/src/main/res/values-zh-rCN/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-zh-rCN/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance 应用微件错误"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"运行 "<b><tt>"adb logcat"</tt></b>" 后搜索 "<b><tt>"GlanceAppWidget"</tt></b>" 可查看确切错误"</string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"无法显示内容"</string>
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-zh-rHK/strings.xml b/glance/glance-appwidget/src/main/res/values-zh-rHK/strings.xml
index 67e937f..5bcdb2b 100644
--- a/glance/glance-appwidget/src/main/res/values-zh-rHK/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-zh-rHK/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance 應用程式小工具錯誤"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"使用 "<b><tt>"adb logcat"</tt></b>" 搜尋 "<b><tt>"GlanceAppWidget"</tt></b>",查看確實的錯誤資料"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-zh-rTW/strings.xml b/glance/glance-appwidget/src/main/res/values-zh-rTW/strings.xml
index 3b2ad0c..02605dc 100644
--- a/glance/glance-appwidget/src/main/res/values-zh-rTW/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-zh-rTW/strings.xml
@@ -19,4 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Glance 應用程式小工具錯誤"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"搜尋 "<b><tt>"GlanceAppWidget"</tt></b>" 時,你可以使用 "<b><tt>"adb logcat"</tt></b>" 查看確切的錯誤資訊"</string>
+    <!-- no translation found for glance_error_layout_text_v2 (5191168365305634625) -->
+    <skip />
 </resources>
diff --git a/glance/glance-appwidget/src/main/res/values-zu/strings.xml b/glance/glance-appwidget/src/main/res/values-zu/strings.xml
index 732c6d7..4df3c48 100644
--- a/glance/glance-appwidget/src/main/res/values-zu/strings.xml
+++ b/glance/glance-appwidget/src/main/res/values-zu/strings.xml
@@ -19,4 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="glance_error_layout_title" msgid="3631961919234443531"><b>"Iphutha le-App Yewijethi Yokubuka Nje"</b></string>
     <string name="glance_error_layout_text" msgid="2863935784364843033">"Hlola iphutha okuyilona usebenzisa i-"<b><tt>"adb logcat"</tt></b>", esesha i-"<b><tt>"GlanceAppWidget"</tt></b></string>
+    <string name="glance_error_layout_text_v2" msgid="5191168365305634625">"Ayikwazi ukubonisa okuqukethwe"</string>
 </resources>
diff --git a/glance/glance-material/src/main/java/androidx/glance/material/MaterialThemes.kt b/glance/glance-material/src/main/java/androidx/glance/material/MaterialThemes.kt
index 8ef025e..bab6696 100644
--- a/glance/glance-material/src/main/java/androidx/glance/material/MaterialThemes.kt
+++ b/glance/glance-material/src/main/java/androidx/glance/material/MaterialThemes.kt
@@ -71,6 +71,7 @@
         inverseOnSurface = ColorProvider(ColorNotDefined, ColorNotDefined),
         inverseSurface = ColorProvider(ColorNotDefined, ColorNotDefined),
         inversePrimary = ColorProvider(ColorNotDefined, ColorNotDefined),
+        widgetBackground = background,
     )
 }
 
@@ -119,6 +120,7 @@
         inverseOnSurface = ColorProvider(ColorNotDefined, ColorNotDefined),
         inverseSurface = ColorProvider(ColorNotDefined, ColorNotDefined),
         inversePrimary = ColorProvider(ColorNotDefined, ColorNotDefined),
+        widgetBackground = background,
     )
 }
 
diff --git a/glance/glance-material3/build.gradle b/glance/glance-material3/build.gradle
index 22a972f..893f5b8 100644
--- a/glance/glance-material3/build.gradle
+++ b/glance/glance-material3/build.gradle
@@ -20,6 +20,7 @@
     api("androidx.compose.runtime:runtime:1.1.1")
     api(project(":glance:glance"))
     implementation("androidx.compose.material3:material3:1.0.0")
+    implementation("androidx.core:core:1.12.0")
 }
 
 android {
diff --git a/glance/glance-material3/src/main/java/androidx/glance/material3/Material3Themes.kt b/glance/glance-material3/src/main/java/androidx/glance/material3/Material3Themes.kt
index ea90a7c..b0e8eec 100644
--- a/glance/glance-material3/src/main/java/androidx/glance/material3/Material3Themes.kt
+++ b/glance/glance-material3/src/main/java/androidx/glance/material3/Material3Themes.kt
@@ -17,6 +17,10 @@
 package androidx.glance.material3
 
 import androidx.compose.material3.ColorScheme
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import androidx.core.graphics.ColorUtils.M3HCTToColor
+import androidx.core.graphics.ColorUtils.colorToM3HCT
 import androidx.glance.color.ColorProvider
 import androidx.glance.color.ColorProviders
 import androidx.glance.color.colorProviders
@@ -81,6 +85,12 @@
         ),
         inverseSurface = ColorProvider(day = light.inverseSurface, night = dark.inverseSurface),
         inversePrimary = ColorProvider(day = light.inversePrimary, night = dark.inversePrimary),
+        // Widget background is a widget / glace specific token it is generally derived from the
+        // secondary container color.
+        widgetBackground = ColorProvider(
+            day = adjustColorToneForWidgetBackground(light.secondaryContainer),
+            night = adjustColorToneForWidgetBackground(dark.secondaryContainer)
+        ),
     )
 }
 
@@ -116,5 +126,33 @@
         inverseOnSurface = ColorProvider(color = scheme.inverseOnSurface),
         inverseSurface = ColorProvider(color = scheme.inverseSurface),
         inversePrimary = ColorProvider(color = scheme.inversePrimary),
+
+        // Widget background is a widget / glace specific token it is generally derived from the
+        // secondary container color.
+        widgetBackground = ColorProvider(
+            color = adjustColorToneForWidgetBackground(scheme.secondaryContainer))
     )
 }
+
+private const val WIDGET_BG_TONE_ADJUSTMENT_LIGHT = 5f
+private const val WIDGET_BG_TONE_ADJUSTMENT_DARK = -10f
+
+/**
+ * Adjusts the input color to work as a widgetBackground token.
+ *
+ * widgetBackground is a Widgets / Glance specific role so won't be present in the original Scheme.
+ * In the system it is defined as being a variation on the secondaryContainer, lighter for light
+ * themes and darker for dark themes.
+ */
+private fun adjustColorToneForWidgetBackground(input: Color): Color {
+    val hctColor = floatArrayOf(0f, 0f, 0f)
+    colorToM3HCT(input.toArgb(), hctColor)
+    // Check the Tone of the input color, if it is "light" (greater than 50) lighten it, otherwise
+    // darken it.
+    val adjustment =
+        if (hctColor[2] > 50) WIDGET_BG_TONE_ADJUSTMENT_LIGHT else WIDGET_BG_TONE_ADJUSTMENT_DARK
+
+    // Tone should be defined in the 0 - 100 range, ok to clamp here.
+    val tone = (hctColor[2] + adjustment).coerceIn(0f, 100f)
+    return Color(M3HCTToColor(hctColor[0], hctColor[1], tone))
+}
diff --git a/glance/glance-template/build.gradle b/glance/glance-template/build.gradle
index dec7cd8..cc9ddd7 100644
--- a/glance/glance-template/build.gradle
+++ b/glance/glance-template/build.gradle
@@ -49,7 +49,7 @@
     implementation(libs.kotlinStdlib)
 
     // Force upgrade since 1.2.0 is not compatible with latest lint.
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
 
     testImplementation(libs.robolectric)
     testImplementation(libs.testCore)
diff --git a/glance/glance/api/current.txt b/glance/glance/api/current.txt
index 84d7c07..dd31b97 100644
--- a/glance/glance/api/current.txt
+++ b/glance/glance/api/current.txt
@@ -217,6 +217,7 @@
     method public final androidx.glance.unit.ColorProvider getSurfaceVariant();
     method public final androidx.glance.unit.ColorProvider getTertiary();
     method public final androidx.glance.unit.ColorProvider getTertiaryContainer();
+    method public final androidx.glance.unit.ColorProvider getWidgetBackground();
     property public final androidx.glance.unit.ColorProvider background;
     property public final androidx.glance.unit.ColorProvider error;
     property public final androidx.glance.unit.ColorProvider errorContainer;
@@ -243,10 +244,12 @@
     property public final androidx.glance.unit.ColorProvider surfaceVariant;
     property public final androidx.glance.unit.ColorProvider tertiary;
     property public final androidx.glance.unit.ColorProvider tertiaryContainer;
+    property public final androidx.glance.unit.ColorProvider widgetBackground;
   }
 
   public final class ColorProvidersKt {
     method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
+    method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary, androidx.glance.unit.ColorProvider widgetBackground);
   }
 
   public final class DayNightColorProvidersKt {
diff --git a/glance/glance/api/restricted_current.txt b/glance/glance/api/restricted_current.txt
index 84d7c07..dd31b97 100644
--- a/glance/glance/api/restricted_current.txt
+++ b/glance/glance/api/restricted_current.txt
@@ -217,6 +217,7 @@
     method public final androidx.glance.unit.ColorProvider getSurfaceVariant();
     method public final androidx.glance.unit.ColorProvider getTertiary();
     method public final androidx.glance.unit.ColorProvider getTertiaryContainer();
+    method public final androidx.glance.unit.ColorProvider getWidgetBackground();
     property public final androidx.glance.unit.ColorProvider background;
     property public final androidx.glance.unit.ColorProvider error;
     property public final androidx.glance.unit.ColorProvider errorContainer;
@@ -243,10 +244,12 @@
     property public final androidx.glance.unit.ColorProvider surfaceVariant;
     property public final androidx.glance.unit.ColorProvider tertiary;
     property public final androidx.glance.unit.ColorProvider tertiaryContainer;
+    property public final androidx.glance.unit.ColorProvider widgetBackground;
   }
 
   public final class ColorProvidersKt {
     method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
+    method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary, androidx.glance.unit.ColorProvider widgetBackground);
   }
 
   public final class DayNightColorProvidersKt {
diff --git a/glance/glance/build.gradle b/glance/glance/build.gradle
index 43f0cde..3b10987 100644
--- a/glance/glance/build.gradle
+++ b/glance/glance/build.gradle
@@ -46,7 +46,7 @@
     implementation(libs.kotlinStdlib)
 
     // Force upgrade since 1.2.0 is not compatible with latest lint.
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
 
     testImplementation(libs.robolectric)
     testImplementation(libs.testCore)
diff --git a/glance/glance/src/main/java/androidx/glance/color/ColorProviders.kt b/glance/glance/src/main/java/androidx/glance/color/ColorProviders.kt
index 73815bc..e3ba0b5 100644
--- a/glance/glance/src/main/java/androidx/glance/color/ColorProviders.kt
+++ b/glance/glance/src/main/java/androidx/glance/color/ColorProviders.kt
@@ -17,6 +17,7 @@
 package androidx.glance.color
 
 import androidx.annotation.RestrictTo
+import androidx.compose.ui.graphics.Color
 import androidx.glance.R
 import androidx.glance.unit.ColorProvider
 
@@ -24,34 +25,151 @@
  * Holds a set of Glance specific [ColorProvider] that can be used to represent a Material 3 color
  * scheme.
  */
-sealed class ColorProviders(
-    val primary: ColorProvider,
-    val onPrimary: ColorProvider,
-    val primaryContainer: ColorProvider,
-    val onPrimaryContainer: ColorProvider,
-    val secondary: ColorProvider,
-    val onSecondary: ColorProvider,
-    val secondaryContainer: ColorProvider,
-    val onSecondaryContainer: ColorProvider,
-    val tertiary: ColorProvider,
-    val onTertiary: ColorProvider,
-    val tertiaryContainer: ColorProvider,
-    val onTertiaryContainer: ColorProvider,
-    val error: ColorProvider,
-    val errorContainer: ColorProvider,
-    val onError: ColorProvider,
-    val onErrorContainer: ColorProvider,
-    val background: ColorProvider,
-    val onBackground: ColorProvider,
-    val surface: ColorProvider,
-    val onSurface: ColorProvider,
-    val surfaceVariant: ColorProvider,
-    val onSurfaceVariant: ColorProvider,
-    val outline: ColorProvider,
-    val inverseOnSurface: ColorProvider,
-    val inverseSurface: ColorProvider,
+sealed class ColorProviders {
+
+    val primary: ColorProvider
+    val onPrimary: ColorProvider
+    val primaryContainer: ColorProvider
+    val onPrimaryContainer: ColorProvider
+    val secondary: ColorProvider
+    val onSecondary: ColorProvider
+    val secondaryContainer: ColorProvider
+    val onSecondaryContainer: ColorProvider
+    val tertiary: ColorProvider
+    val onTertiary: ColorProvider
+    val tertiaryContainer: ColorProvider
+    val onTertiaryContainer: ColorProvider
+    val error: ColorProvider
+    val errorContainer: ColorProvider
+    val onError: ColorProvider
+    val onErrorContainer: ColorProvider
+    val background: ColorProvider
+    val onBackground: ColorProvider
+    val surface: ColorProvider
+    val onSurface: ColorProvider
+    val surfaceVariant: ColorProvider
+    val onSurfaceVariant: ColorProvider
+    val outline: ColorProvider
+    val inverseOnSurface: ColorProvider
+    val inverseSurface: ColorProvider
     val inversePrimary: ColorProvider
-) {
+    val widgetBackground: ColorProvider
+
+    constructor(
+        primary: ColorProvider,
+        onPrimary: ColorProvider,
+        primaryContainer: ColorProvider,
+        onPrimaryContainer: ColorProvider,
+        secondary: ColorProvider,
+        onSecondary: ColorProvider,
+        secondaryContainer: ColorProvider,
+        onSecondaryContainer: ColorProvider,
+        tertiary: ColorProvider,
+        onTertiary: ColorProvider,
+        tertiaryContainer: ColorProvider,
+        onTertiaryContainer: ColorProvider,
+        error: ColorProvider,
+        errorContainer: ColorProvider,
+        onError: ColorProvider,
+        onErrorContainer: ColorProvider,
+        background: ColorProvider,
+        onBackground: ColorProvider,
+        surface: ColorProvider,
+        onSurface: ColorProvider,
+        surfaceVariant: ColorProvider,
+        onSurfaceVariant: ColorProvider,
+        outline: ColorProvider,
+        inverseOnSurface: ColorProvider,
+        inverseSurface: ColorProvider,
+        inversePrimary: ColorProvider,
+        widgetBackground: ColorProvider,
+    ) {
+        this.primary = primary
+        this.onPrimary = onPrimary
+        this.primaryContainer = primaryContainer
+        this.onPrimaryContainer = onPrimaryContainer
+        this.secondary = secondary
+        this.onSecondary = onSecondary
+        this.secondaryContainer = secondaryContainer
+        this.onSecondaryContainer = onSecondaryContainer
+        this.tertiary = tertiary
+        this.onTertiary = onTertiary
+        this.tertiaryContainer = tertiaryContainer
+        this.onTertiaryContainer = onTertiaryContainer
+        this.error = error
+        this.errorContainer = errorContainer
+        this.onError = onError
+        this.onErrorContainer = onErrorContainer
+        this.background = background
+        this.onBackground = onBackground
+        this.surface = surface
+        this.onSurface = onSurface
+        this.surfaceVariant = surfaceVariant
+        this.onSurfaceVariant = onSurfaceVariant
+        this.outline = outline
+        this.inverseOnSurface = inverseOnSurface
+        this.inverseSurface = inverseSurface
+        this.inversePrimary = inversePrimary
+        this.widgetBackground = widgetBackground
+    }
+
+    @Deprecated("Hidden")
+    constructor(
+        primary: ColorProvider,
+        onPrimary: ColorProvider,
+        primaryContainer: ColorProvider,
+        onPrimaryContainer: ColorProvider,
+        secondary: ColorProvider,
+        onSecondary: ColorProvider,
+        secondaryContainer: ColorProvider,
+        onSecondaryContainer: ColorProvider,
+        tertiary: ColorProvider,
+        onTertiary: ColorProvider,
+        tertiaryContainer: ColorProvider,
+        onTertiaryContainer: ColorProvider,
+        error: ColorProvider,
+        errorContainer: ColorProvider,
+        onError: ColorProvider,
+        onErrorContainer: ColorProvider,
+        background: ColorProvider,
+        onBackground: ColorProvider,
+        surface: ColorProvider,
+        onSurface: ColorProvider,
+        surfaceVariant: ColorProvider,
+        onSurfaceVariant: ColorProvider,
+        outline: ColorProvider,
+        inverseOnSurface: ColorProvider,
+        inverseSurface: ColorProvider,
+        inversePrimary: ColorProvider,
+    ) {
+        this.primary = primary
+        this.onPrimary = onPrimary
+        this.primaryContainer = primaryContainer
+        this.onPrimaryContainer = onPrimaryContainer
+        this.secondary = secondary
+        this.onSecondary = onSecondary
+        this.secondaryContainer = secondaryContainer
+        this.onSecondaryContainer = onSecondaryContainer
+        this.tertiary = tertiary
+        this.onTertiary = onTertiary
+        this.tertiaryContainer = tertiaryContainer
+        this.onTertiaryContainer = onTertiaryContainer
+        this.error = error
+        this.errorContainer = errorContainer
+        this.onError = onError
+        this.onErrorContainer = onErrorContainer
+        this.background = background
+        this.onBackground = onBackground
+        this.surface = surface
+        this.onSurface = onSurface
+        this.surfaceVariant = surfaceVariant
+        this.onSurfaceVariant = onSurfaceVariant
+        this.outline = outline
+        this.inverseOnSurface = inverseOnSurface
+        this.inverseSurface = inverseSurface
+        this.inversePrimary = inversePrimary
+        this.widgetBackground = ColorProvider(Color.Unspecified)
+    }
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
@@ -85,6 +203,7 @@
         if (inverseOnSurface != other.inverseOnSurface) return false
         if (inverseSurface != other.inverseSurface) return false
         if (inversePrimary != other.inversePrimary) return false
+        if (widgetBackground != other.widgetBackground) return false
 
         return true
     }
@@ -116,6 +235,7 @@
         result = 31 * result + inverseOnSurface.hashCode()
         result = 31 * result + inverseSurface.hashCode()
         result = 31 * result + inversePrimary.hashCode()
+        result = 31 * result + widgetBackground.hashCode()
         return result
     }
 
@@ -145,7 +265,8 @@
             "outline=$outline, " +
             "inverseOnSurface=$inverseOnSurface, " +
             "inverseSurface=$inverseSurface, " +
-            "inversePrimary=$inversePrimary)"
+            "inversePrimary=$inversePrimary)" +
+            "widgetBackground=$widgetBackground"
     }
 }
 
@@ -182,6 +303,7 @@
     inverseOnSurface = ColorProvider(R.color.glance_colorOnSurfaceInverse),
     inverseSurface = ColorProvider(R.color.glance_colorSurfaceInverse),
     inversePrimary = ColorProvider(R.color.glance_colorPrimaryInverse),
+    widgetBackground = ColorProvider(R.color.glance_colorWidgetBackground),
 )
 
 internal class CustomColorProviders(
@@ -210,7 +332,8 @@
     outline: ColorProvider,
     inverseOnSurface: ColorProvider,
     inverseSurface: ColorProvider,
-    inversePrimary: ColorProvider
+    inversePrimary: ColorProvider,
+    widgetBackground: ColorProvider,
 ) : ColorProviders(
     primary = primary,
     onPrimary = onPrimary,
@@ -237,7 +360,8 @@
     outline = outline,
     inverseOnSurface = inverseOnSurface,
     inverseSurface = inverseSurface,
-    inversePrimary = inversePrimary
+    inversePrimary = inversePrimary,
+    widgetBackground = widgetBackground,
 )
 
 fun colorProviders(
@@ -266,7 +390,8 @@
     outline: ColorProvider,
     inverseOnSurface: ColorProvider,
     inverseSurface: ColorProvider,
-    inversePrimary: ColorProvider
+    inversePrimary: ColorProvider,
+    widgetBackground: ColorProvider,
 ): ColorProviders = CustomColorProviders(
     primary = primary,
     onPrimary = onPrimary,
@@ -293,5 +418,63 @@
     outline = outline,
     inverseOnSurface = inverseOnSurface,
     inverseSurface = inverseSurface,
-    inversePrimary = inversePrimary
+    inversePrimary = inversePrimary,
+    widgetBackground = widgetBackground,
+)
+
+fun colorProviders(
+    primary: ColorProvider,
+    onPrimary: ColorProvider,
+    primaryContainer: ColorProvider,
+    onPrimaryContainer: ColorProvider,
+    secondary: ColorProvider,
+    onSecondary: ColorProvider,
+    secondaryContainer: ColorProvider,
+    onSecondaryContainer: ColorProvider,
+    tertiary: ColorProvider,
+    onTertiary: ColorProvider,
+    tertiaryContainer: ColorProvider,
+    onTertiaryContainer: ColorProvider,
+    error: ColorProvider,
+    errorContainer: ColorProvider,
+    onError: ColorProvider,
+    onErrorContainer: ColorProvider,
+    background: ColorProvider,
+    onBackground: ColorProvider,
+    surface: ColorProvider,
+    onSurface: ColorProvider,
+    surfaceVariant: ColorProvider,
+    onSurfaceVariant: ColorProvider,
+    outline: ColorProvider,
+    inverseOnSurface: ColorProvider,
+    inverseSurface: ColorProvider,
+    inversePrimary: ColorProvider,
+): ColorProviders = CustomColorProviders(
+    primary = primary,
+    onPrimary = onPrimary,
+    primaryContainer = primaryContainer,
+    onPrimaryContainer = onPrimaryContainer,
+    secondary = secondary,
+    onSecondary = onSecondary,
+    secondaryContainer = secondaryContainer,
+    onSecondaryContainer = onSecondaryContainer,
+    tertiary = tertiary,
+    onTertiary = onTertiary,
+    tertiaryContainer = tertiaryContainer,
+    onTertiaryContainer = onTertiaryContainer,
+    error = error,
+    errorContainer = errorContainer,
+    onError = onError,
+    onErrorContainer = onErrorContainer,
+    background = background,
+    onBackground = onBackground,
+    surface = surface,
+    onSurface = onSurface,
+    surfaceVariant = surfaceVariant,
+    onSurfaceVariant = onSurfaceVariant,
+    outline = outline,
+    inverseOnSurface = inverseOnSurface,
+    inverseSurface = inverseSurface,
+    inversePrimary = inversePrimary,
+    widgetBackground = ColorProvider(Color.Unspecified),
 )
diff --git a/glance/glance/src/main/res/values-night-v31/colors.xml b/glance/glance/src/main/res/values-night-v31/colors.xml
index bbf8abd..b025d91 100644
--- a/glance/glance/src/main/res/values-night-v31/colors.xml
+++ b/glance/glance/src/main/res/values-night-v31/colors.xml
@@ -41,4 +41,5 @@
     <color name="glance_colorOnError">#ff601410</color>
     <color name="glance_colorErrorContainer">#ff8c1d18</color>
     <color name="glance_colorOnErrorContainer">#fff2b8b5</color>
+    <color name="glance_colorWidgetBackground">@android:color/system_accent2_800</color>
 </resources>
\ No newline at end of file
diff --git a/glance/glance/src/main/res/values-night/colors.xml b/glance/glance/src/main/res/values-night/colors.xml
index 990cfd3..7669747 100644
--- a/glance/glance/src/main/res/values-night/colors.xml
+++ b/glance/glance/src/main/res/values-night/colors.xml
@@ -42,4 +42,5 @@
     <color name="glance_colorOnError">#ff601410</color>
     <color name="glance_colorErrorContainer">#ff8c1d18</color>
     <color name="glance_colorOnErrorContainer">#fff2b8b5</color>
+    <color name="glance_colorWidgetBackground">#ff20333d</color>
 </resources>
\ No newline at end of file
diff --git a/glance/glance/src/main/res/values-v31/colors.xml b/glance/glance/src/main/res/values-v31/colors.xml
index fc1a525..9a4cca9 100644
--- a/glance/glance/src/main/res/values-v31/colors.xml
+++ b/glance/glance/src/main/res/values-v31/colors.xml
@@ -41,4 +41,5 @@
     <color name="glance_colorOnError">#ffffffff</color>
     <color name="glance_colorErrorContainer">#fff9dedc</color>
     <color name="glance_colorOnErrorContainer">#ff410e0b</color>
+    <color name="glance_colorWidgetBackground">@android:color/system_accent2_50</color>
 </resources>
\ No newline at end of file
diff --git a/glance/glance/src/main/res/values/colors.xml b/glance/glance/src/main/res/values/colors.xml
index bfc0020..773c831 100644
--- a/glance/glance/src/main/res/values/colors.xml
+++ b/glance/glance/src/main/res/values/colors.xml
@@ -42,4 +42,5 @@
     <color name="glance_colorOnError">#ffffffff</color>
     <color name="glance_colorErrorContainer">#fff9dedc</color>
     <color name="glance_colorOnErrorContainer">#ff410e0b</color>
+    <color name="glance_colorWidgetBackground">#ffe0f3ff</color>
 </resources>
\ No newline at end of file
diff --git a/glance/glance/src/test/kotlin/androidx/glance/color/ColorProvidersTest.kt b/glance/glance/src/test/kotlin/androidx/glance/color/ColorProvidersTest.kt
index 64bb914..1a3bf1e 100644
--- a/glance/glance/src/test/kotlin/androidx/glance/color/ColorProvidersTest.kt
+++ b/glance/glance/src/test/kotlin/androidx/glance/color/ColorProvidersTest.kt
@@ -328,7 +328,8 @@
                 outline = testColor,
                 inverseOnSurface = testColor,
                 inverseSurface = testColor,
-                inversePrimary = testColor
+                inversePrimary = testColor,
+                widgetBackground = testColor
             )
         )
     }
diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys
index 35cbf8c..7c4300e 100644
--- a/gradle/verification-keyring.keys
+++ b/gradle/verification-keyring.keys
@@ -7500,6 +7500,35 @@
 =SIQO
 -----END PGP PUBLIC KEY BLOCK-----
 
+pub    4D176DC503FB4267
+uid    Colin White <colin@colinwhite.me>
+
+sub    5686B45C142551D3
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQENBF1EtnUBCAChtyYd/4eMAxTz5DVmO+8QOrTA1cf9bprQhtXD5pVbw8/IGKN+
+EqXmvt7AGy+4O633g7ec5iyirwCfEP+4YDv8k1LOvY9C5+tOwfK+FxAPRVc1AAB5
+q23x4yMI7aDdvN52/jqpREeBBWPcrnEIET68RApayALenjUP6Kx5K0ge9aU8uaSB
+HsVpHSr05L1nKuuqh0LDNXIYDLCpo0LnwPNfLrJ2xT6gNTwHBY5mncDy4m01rdxD
+3to/s2Uu/9Xkfz3+BJKRD2kr5txiqoW69H/qsg7u8tTOr9FhB1T21pjFZsdpzbOq
+cKd7UyD0RpyQYHmTeUIhKSI0Su7pKT+RTL9BABEBAAG0IUNvbGluIFdoaXRlIDxj
+b2xpbkBjb2xpbndoaXRlLm1lPrkBDQRdRLZ1AQgAxOP3klOByUo1Kyl1O6rZqj9e
+yts/oXtuDTISfcRkZyg6fmkhT4kpd4xLSP3xHvRwJugybyTedDHzXXCOSjl3EFfU
+GaKJuMqSKs5YjQOWk8S9BegAPeiq6PGV6gbHZQ3Xqy+XE+TLy5N96zu7th/YJraE
+NpS79sj//mJQE2d49YrxhZwtMj64X0B8/mDmED+D2cPXAoLxNh//LaV26gpOC9yV
+YTXrCq7ODPE5LyoljhBmPZxoapcn/39V6UvVSG8Dq6R5QdahDgNnCEjVPtSzGtbC
+B4zpSv/LvZkm8gKlaxUcra5clXL7p4NWGx1C1Ap4+H6U2h53lC7FuYl8k9mBNwAR
+AQABiQE2BBgBCAAgAhsMFiEE3yXTxIaP7BdxgvrWTRdtxQP7QmcFAl1P4JcACgkQ
+TRdtxQP7QmcrkAgAiSZ2VRgHTY8SV4dXXjYBO6WM2Yzamt4bO5Uqflw7gPO44AtJ
+h+Pn89iz37RnGL+bAq2MCNsgV4G+KaLJrE0c/rt/szC7UmwEJmy4DcH5B80wzBvo
+2kpN1Uyr7+1+IgHogL6HjLxccoBpawpm0AnhNYrd2ml7STBCfNmilztWGrTkerb/
+2qOY7PmgWEad7whM0OEj1OE+M2IYZUga3gxf0B94GtQNwmI4xv5qXGskTFAlM9ko
+94NfvOxEkgCGtnicN4Md74od0POEVrZxBpXuJGt60XVGvOr1iLmlpypWo+PkHhZe
+Wt45mNAFy6vraTYYCkGXQKqx1kxg+OurYgIFgw==
+=zwEJ
+-----END PGP PUBLIC KEY BLOCK-----
+
 pub    4DB7BC57DFDBCEA4
 uid    Timothy Wall <twall@users.sf.net>
 
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 2ae8411..4061606 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -431,6 +431,7 @@
             <trusting group="com.beust"/>
             <trusting group="org.testng"/>
          </trusted-key>
+         <trusted-key id="DF25D3C4868FEC177182FAD64D176DC503FB4267" group="io.coil-kt"/>
          <trusted-key id="DF3986523A6AD079C46B730BCA183FBA1E476C6E" group="com.squareup.leakcanary"/>
          <trusted-key id="E01ED293981AE484403B65D7DA70BCBA6D76AD03" group="com.charleskorn.kaml"/>
          <trusted-key id="E0D98C5FD55A8AF232290E58DEE12B9896F97E34" group="org.pcollections"/>
@@ -628,6 +629,11 @@
             <sha256 value="02c12c3c2ae12dd475219ff691c82a4d9ea21f44bc594a181295bf6d43dcfbb0" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
+      <component group="com.google.guava" name="guava" version="32.1.3-android">
+         <artifact name="guava-32.1.3-jre.jar">
+            <sha256 value="6d4e2b5a118aab62e6e5e29d185a0224eed82c85c40ac3d33cf04a270c3b3744" origin="Generated by Gradle" reason="Artifact is not signed"/>
+         </artifact>
+      </component>
       <component group="com.google.prefab" name="cli" version="2.0.0">
          <artifact name="cli-2.0.0-all.jar">
             <sha256 value="d9bd89f68446b82be038aae774771ad85922d0b375209b17625a2734b5317e29" origin="Generated by Gradle" reason="https://github.com/google/prefab/issues/157"/>
diff --git a/graphics/filters/filters/build.gradle b/graphics/filters/filters/build.gradle
index d6406c1..338ef20 100644
--- a/graphics/filters/filters/build.gradle
+++ b/graphics/filters/filters/build.gradle
@@ -43,7 +43,7 @@
     implementation('androidx.media3:media3-transformer:' + media3Version)
 
     // Force upgrade since 1.2.0 is not compatible with latest lint.
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
 
     // Test dependencies
     androidTestImplementation(libs.testExtJunit)
diff --git a/graphics/graphics-core/build.gradle b/graphics/graphics-core/build.gradle
index bc917ea..45ad794 100644
--- a/graphics/graphics-core/build.gradle
+++ b/graphics/graphics-core/build.gradle
@@ -33,7 +33,7 @@
 dependencies {
     api(libs.kotlinStdlib)
     implementation(libs.kotlinCoroutinesAndroid)
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
     implementation("androidx.core:core:1.8.0")
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
diff --git a/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/MorphTest.kt b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/MorphTest.kt
new file mode 100644
index 0000000..2462d63
--- /dev/null
+++ b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/MorphTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.graphics.shapes
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Path
+import androidx.core.graphics.scaleMatrix
+import androidx.test.filters.SmallTest
+import org.junit.Test
+
+@SmallTest
+class MorphTest {
+
+    val RADIUS = 50
+    val SCALE = RADIUS.toFloat()
+
+    val poly1 = RoundedPolygon(3, centerX = .5f, centerY = .5f)
+    val poly2 = RoundedPolygon(4, centerX = .5f, centerY = .5f)
+    val morph11 = Morph(poly1, poly1)
+    val morph12 = Morph(poly1, poly2)
+
+    /**
+     * Simple test to verify that a Morph with the same start and end shape has curves
+     * equivalent to those in that shape.
+     */
+    @Test
+    fun cubicsTest() {
+        val p1Cubics = poly1.cubics
+        val cubics11 = morph11.asCubics(0f)
+        assert(cubics11.size > 0)
+        // The structure of a morph and its component shapes may not match exactly, because morph
+        // calculations may optimize some of the zero-length curves out. But in general, every
+        // curve in the morph *should* exist somewhere in the shape it is based on, so we
+        // do an exhaustive search for such existence. Note that this assertion only works because
+        // we constructed the Morph from/to the same shape. A Morph between different shapes
+        // may not have the curves replicated exactly.
+        for (morphCubic in cubics11) {
+            var matched = false
+            for (p1Cubic in p1Cubics) {
+                if (cubicsEqualish(morphCubic, p1Cubic)) {
+                    matched = true
+                    continue
+                }
+            }
+            assert(matched)
+        }
+    }
+
+    /**
+     * This test checks to see whether a morph between two different polygons is correct
+     * at the start (progress 0) and end (progress 1). The actual cubics of the morph vs the
+     * polygons it was constructed from may differ, due to the way the morph is constructed,
+     * but the rendering result should be the same.
+     */
+    @Test
+    fun morphDrawingTest() {
+        // Shapes are in canonical size of 2x2 around center (.5, .5). Translate/scale to
+        // get a larger path
+        val m = scaleMatrix(SCALE, SCALE)
+        m.postTranslate(SCALE / 2f, SCALE / 2F)
+        val poly1Path = poly1.toPath()
+        poly1Path.transform(m)
+        val poly2Path = poly2.toPath()
+        poly2Path.transform(m)
+        val morph120Path = morph12.toPath(0f)
+        morph120Path.transform(m)
+        val morph121Path = morph12.toPath(1f)
+        morph121Path.transform(m)
+
+        val polyBitmap = Bitmap.createBitmap(RADIUS * 2, RADIUS * 2, Bitmap.Config.ARGB_8888)
+        val morphBitmap = Bitmap.createBitmap(RADIUS * 2, RADIUS * 2, Bitmap.Config.ARGB_8888)
+        val polyCanvas = Canvas(polyBitmap)
+        val morphCanvas = Canvas(morphBitmap)
+
+        // Check that the morph at progress 0 is equivalent to poly1
+        drawTestPath(polyCanvas, poly1Path)
+        drawTestPath(morphCanvas, morph120Path)
+        assertBitmapsEqual(polyBitmap, morphBitmap)
+
+        // Check that the morph at progress 1 is equivalent to poly2
+        drawTestPath(polyCanvas, poly2Path)
+        drawTestPath(morphCanvas, morph121Path)
+        assertBitmapsEqual(polyBitmap, morphBitmap)
+    }
+
+    /**
+     * Utility function - Fill the canvas with black and draw the path in white.
+     */
+    private fun drawTestPath(canvas: Canvas, path: Path) {
+        canvas.drawColor(Color.BLACK)
+        val paint = Paint()
+        paint.isAntiAlias = false
+        paint.style = Paint.Style.FILL
+        paint.color = Color.WHITE
+        canvas.drawPath(path, paint)
+    }
+}
diff --git a/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt
index 4ad149c..28143e6 100644
--- a/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt
+++ b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt
@@ -145,18 +145,23 @@
     fun featuresTest() {
         val squareFeatures = square.features
 
-        // Verify that cubics of polygon == cubics of features of that polygon
-        assertTrue(square.cubics == squareFeatures.flatMap { it.cubics })
+        // Verify that cubics of polygon == nonzero cubics of features of that polygon
+        // Note the Equalish test since some points may be adjusted in conversion from raw
+        // cubics in the feature to the cubics list for the shape
+        var nonzeroCubics = nonzeroCubics(squareFeatures.flatMap { it.cubics })
+        assertCubicListsEqualish(square.cubics, nonzeroCubics)
 
         // Same as above but with rounded corners
         val roundedSquare = RoundedPolygon(4, rounding = CornerRounding(.1f))
         val roundedFeatures = roundedSquare.features
-        assertTrue(roundedSquare.cubics == roundedFeatures.flatMap { it.cubics })
+        nonzeroCubics = nonzeroCubics(roundedFeatures.flatMap { it.cubics })
+        assertCubicListsEqualish(roundedSquare.cubics, nonzeroCubics)
 
         // Same as the first polygon test, but with a copy of that polygon
         val squareCopy = RoundedPolygon(square)
         val squareCopyFeatures = squareCopy.features
-        assertTrue(squareCopy.cubics == squareCopyFeatures.flatMap { it.cubics })
+        nonzeroCubics = nonzeroCubics(squareCopyFeatures.flatMap { it.cubics })
+        assertCubicListsEqualish(squareCopy.cubics, nonzeroCubics)
 
         // Test other elements of Features
         val translator = translateTransform(1f, 2f)
@@ -182,4 +187,12 @@
         assertNotEquals(preTransformVertices, postTransformVertices)
         assertNotEquals(preTransformCenters, postTransformCenters)
     }
+
+    private fun nonzeroCubics(original: List<Cubic>): List<Cubic> {
+        val result = mutableListOf<Cubic>()
+        for (i in original.indices) {
+            if (!original[i].zeroLength()) result.add(original[i])
+        }
+        return result
+    }
 }
diff --git a/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/TestUtils.kt b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/TestUtils.kt
index 17b5527..2f28cec 100644
--- a/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/TestUtils.kt
+++ b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/TestUtils.kt
@@ -16,6 +16,9 @@
 
 package androidx.graphics.shapes
 
+import android.graphics.Bitmap
+import androidx.core.graphics.get
+import kotlin.math.abs
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 
@@ -28,6 +31,21 @@
     assertEquals(msg, expected.y, actual.y, Epsilon)
 }
 
+internal fun equalish(f0: Float, f1: Float, epsilon: Float): Boolean {
+    return abs(f0 - f1) < epsilon
+}
+
+internal fun pointsEqualish(p0: Point, p1: Point): Boolean {
+    return equalish(p0.x, p1.x, Epsilon) && equalish(p0.y, p1.y, Epsilon)
+}
+
+internal fun cubicsEqualish(c0: Cubic, c1: Cubic): Boolean {
+    return pointsEqualish(Point(c0.anchor0X, c0.anchor0Y), Point(c1.anchor0X, c1.anchor0Y)) &&
+        pointsEqualish(Point(c0.anchor1X, c0.anchor1Y), Point(c1.anchor1X, c1.anchor1Y)) &&
+        pointsEqualish(Point(c0.control0X, c0.control0Y), Point(c1.control0X, c1.control0Y)) &&
+        pointsEqualish(Point(c0.control1X, c0.control1Y), Point(c1.control1X, c1.control1Y))
+}
+
 internal fun assertCubicsEqualish(expected: Cubic, actual: Cubic) {
     assertPointsEqualish(Point(expected.anchor0X, expected.anchor0Y),
         Point(actual.anchor0X, actual.anchor0Y))
@@ -39,6 +57,13 @@
         Point(actual.anchor1X, actual.anchor1Y))
 }
 
+internal fun assertCubicListsEqualish(expected: List<Cubic>, actual: List<Cubic>) {
+    assertEquals(expected.size, actual.size)
+    for (i in expected.indices) {
+        assertCubicsEqualish(expected[i], actual[i])
+    }
+}
+
 internal fun assertPointGreaterish(expected: Point, actual: Point) {
     assertTrue(actual.x >= expected.x - Epsilon)
     assertTrue(actual.y >= expected.y - Epsilon)
@@ -75,3 +100,13 @@
 internal fun translateTransform(dx: Float, dy: Float) = PointTransformer {
     x, y -> TransformResult(x + dx, y + dy)
 }
+
+internal fun assertBitmapsEqual(b0: Bitmap, b1: Bitmap) {
+    assertEquals(b0.width, b1.width)
+    assertEquals(b0.height, b1.height)
+    for (row in 0 until b0.height) {
+        for (col in 0 until b0.width) {
+            assertEquals("Pixels at ($col, $row) not equal", b0.get(col, row), b1.get(col, row))
+        }
+    }
+}
diff --git a/graphics/graphics-shapes/src/androidMain/kotlin/androidx/graphics/shapes/Shapes.android.kt b/graphics/graphics-shapes/src/androidMain/kotlin/androidx/graphics/shapes/Shapes.android.kt
index a714bdd2..af6ba6a 100644
--- a/graphics/graphics-shapes/src/androidMain/kotlin/androidx/graphics/shapes/Shapes.android.kt
+++ b/graphics/graphics-shapes/src/androidMain/kotlin/androidx/graphics/shapes/Shapes.android.kt
@@ -35,7 +35,11 @@
 }
 
 /**
- * Gets a [Path] representation for a [RoundedPolygon] shape.
+ * Gets a [Path] representation for a [RoundedPolygon] shape. Note that there is some rounding
+ * happening (to the nearest thousandth), to work around rendering artifacts introduced by some
+ * points being just slightly off from each other (far less than a pixel). This also allows for
+ * a more optimal path, as redundant curves (usually a single point) can be detected and
+ * not added to the resulting path.
  *
  * @param path an optional [Path] object which, if supplied, will avoid the function having
  * to create a new [Path] object
@@ -63,11 +67,8 @@
             path.moveTo(cubic.anchor0X, cubic.anchor0Y)
             first = false
         }
-        path.cubicTo(
-            cubic.control0X, cubic.control0Y,
-            cubic.control1X, cubic.control1Y,
-            cubic.anchor1X, cubic.anchor1Y
-        )
+        path.cubicTo(cubic.control0X, cubic.control0Y, cubic.control1X, cubic.control1Y,
+            cubic.anchor1X, cubic.anchor1Y)
     }
     path.close()
 }
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Cubic.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Cubic.kt
index 83ec859..644a4b9 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Cubic.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Cubic.kt
@@ -93,7 +93,7 @@
         )
     }
 
-    private fun zeroLength() = abs(anchor0X - anchor1X) < DistanceEpsilon &&
+    internal fun zeroLength() = abs(anchor0X - anchor1X) < DistanceEpsilon &&
             abs(anchor0Y - anchor1Y) < DistanceEpsilon
 
     private fun zeroIsh(value: Float) = abs(value) < DistanceEpsilon
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Morph.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Morph.kt
index d44d4a9..f8a67cf 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Morph.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Morph.kt
@@ -64,15 +64,26 @@
      */
     fun asCubics(progress: Float): List<Cubic> {
         return buildList {
+            // The first/last mechanism here ensures that the final anchor point in the shape
+            // exactly matches the first anchor point. There can be rendering artifacts introduced
+            // by those points being slightly off, even by much less than a pixel
+            var firstCubic: Cubic? = null
+            var lastCubic: Cubic? = null
             for (i in _morphMatch.indices) {
-                Cubic(FloatArray(8) {
+                val cubic = Cubic(FloatArray(8) {
                     interpolate(
                         _morphMatch[i].first.points[it],
                         _morphMatch[i].second.points[it],
                         progress
                     )
                 })
+                if (firstCubic == null) firstCubic = cubic
+                if (lastCubic != null) add(lastCubic)
+                lastCubic = cubic
             }
+            if (lastCubic != null && firstCubic != null) add(Cubic(
+                lastCubic.anchor0X, lastCubic.anchor0Y, lastCubic.control0X, lastCubic.control0Y,
+                lastCubic.control1X, lastCubic.control1Y, firstCubic.anchor0X, firstCubic.anchor0Y))
         }
     }
 
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
index 14d5217..e6d4e82 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
@@ -38,10 +38,25 @@
      * A flattened version of the [Feature]s, as a List<Cubic>.
      */
     val cubics = buildList {
-        // Equivalent to `features.flatMap { it.cubics }` but without Iterator allocation.
+        // The first/last mechanism here ensures that the final anchor point in the shape
+        // exactly matches the first anchor point. There can be rendering artifacts introduced
+        // by those points being slightly off, even by much less than a pixel
+        var firstCubic: Cubic? = null
+        var lastCubic: Cubic? = null
         for (i in features.indices) {
-            addAll(features[i].cubics)
+            val featureCubics = features[i].cubics
+            for (j in featureCubics.indices) {
+                // Skip zero-length curves; they add nothing and can trigger rendering artifacts
+                if (!featureCubics[j].zeroLength()) {
+                    if (lastCubic != null) add(lastCubic)
+                    lastCubic = featureCubics[j]
+                    if (firstCubic == null) firstCubic = featureCubics[j]
+                }
+            }
         }
+        if (lastCubic != null && firstCubic != null) add(Cubic(
+            lastCubic.anchor0X, lastCubic.anchor0Y, lastCubic.control0X, lastCubic.control0Y,
+            lastCubic.control1X, lastCubic.control1Y, firstCubic.anchor0X, firstCubic.anchor0Y))
     }
 
     init {
diff --git a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/Compose.kt b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/Compose.kt
index 205e000..c1ba1a0 100644
--- a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/Compose.kt
+++ b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/Compose.kt
@@ -21,9 +21,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Matrix
-import androidx.compose.ui.graphics.Path
 import androidx.graphics.shapes.Cubic
-import androidx.graphics.shapes.Morph
 import androidx.graphics.shapes.MutableCubic
 import androidx.graphics.shapes.RoundedPolygon
 import androidx.graphics.shapes.TransformResult
@@ -69,47 +67,6 @@
  */
 fun RoundedPolygon.getBounds() = calculateBounds().let { Rect(it[0], it[1], it[2], it[3]) }
 
-/**
- * Function used to create a Path from a list of Cubics.
- */
-fun List<Cubic>.toPath(path: Path = Path()): Path {
-    path.rewind()
-    firstOrNull()?.let { first ->
-        path.moveTo(first.anchor0X, first.anchor0Y)
-    }
-    for (bezier in this) {
-        path.cubicTo(
-            bezier.control0X, bezier.control0Y,
-            bezier.control1X, bezier.control1Y,
-            bezier.anchor1X, bezier.anchor1Y
-        )
-    }
-    path.close()
-    return path
-}
-
-/**
- * Transforms the morph at a given progress into a [Path].
- * It can optionally be scaled, using the origin (0,0) as pivot point.
- */
-fun Morph.toPath(progress: Float, scale: Float = 1f, path: Path = Path()): Path {
-    var first = true
-    path.rewind()
-    forEachCubic(progress) { bezier ->
-        if (first) {
-            path.moveTo(bezier.anchor0X * scale, bezier.anchor0Y * scale)
-            first = false
-        }
-        path.cubicTo(
-            bezier.control0X * scale, bezier.control0Y * scale,
-            bezier.control1X * scale, bezier.control1Y * scale,
-            bezier.anchor1X * scale, bezier.anchor1Y * scale
-        )
-    }
-    path.close()
-    return path
-}
-
 internal const val DEBUG = false
 
 internal inline fun outputToLog(message: String) {
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
index 472a0d0..75a4a0a 100644
--- 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
@@ -17,6 +17,8 @@
 package androidx.graphics.shapes.testcompose
 
 import android.content.Intent
+import android.graphics.Matrix
+import android.graphics.RectF
 import android.os.Bundle
 import androidx.activity.compose.setContent
 import androidx.compose.animation.core.Animatable
@@ -54,34 +56,45 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asComposePath
+import androidx.compose.ui.graphics.drawscope.Fill
 import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.unit.dp
+import androidx.core.graphics.scaleMatrix
 import androidx.fragment.app.FragmentActivity
 import androidx.graphics.shapes.Cubic
 import androidx.graphics.shapes.Morph
 import androidx.graphics.shapes.RoundedPolygon
 import androidx.graphics.shapes.TransformResult
+import androidx.graphics.shapes.toPath
+import kotlin.math.max
 import kotlin.math.min
 import kotlinx.coroutines.launch
 
 @Composable
-fun PolygonComposable(polygon: RoundedPolygon, modifier: Modifier = Modifier) =
-    PolygonComposableImpl(polygon, modifier)
+fun PolygonComposable(
+    polygon: RoundedPolygon,
+    modifier: Modifier = Modifier,
+    stroked: Boolean = false
+) =
+    PolygonComposableImpl(polygon, modifier, stroked = stroked)
 
 @Composable
 private fun MorphComposable(
     morph: Morph,
     progress: Float,
     modifier: Modifier = Modifier,
-    isDebug: Boolean = false
-) = MorphComposableImpl(morph, modifier, isDebug, progress)
+    isDebug: Boolean = false,
+    stroked: Boolean = false
+) = MorphComposableImpl(morph, modifier, isDebug, progress, stroked = stroked)
 
 @Composable
 private fun MorphComposableImpl(
     morph: Morph,
     modifier: Modifier = Modifier,
     isDebug: Boolean = false,
-    progress: Float
+    progress: Float,
+    stroked: Boolean = false
 ) {
     Box(
         modifier
@@ -89,24 +102,43 @@
             .drawWithContent {
                 drawContent()
                 val scale = min(size.width, size.height)
-                val path = morph.toPath(progress, scale)
+                val composePath = setupPath(morph, progress, scale)
                 if (isDebug) {
-                    drawPath(path, Color.Green, style = Stroke(2f))
+                    drawPath(composePath, Color.Green, style = Stroke(2f))
                     morph.forEachCubic(progress) { cubic ->
                         cubic.transform { x, y -> TransformResult(x * scale, y * scale) }
                         debugDraw(cubic)
                     }
                 } else {
-                    drawPath(path, Color.White)
+                    val style = if (stroked) Stroke(size.width / 10f) else Fill
+                    drawPath(composePath, Color.White, style = style)
                 }
             })
 }
 
+private fun setupPath(morph: Morph, progress: Float, viewportSize: Float):
+    androidx.compose.ui.graphics.Path {
+    val path = morph.toPath(progress)
+    val pathBounds = RectF()
+    path.computeBounds(pathBounds, false)
+    val pathSize = max(pathBounds.width(), pathBounds.height())
+    val scaleFactor = viewportSize / pathSize
+    val pathCenterX = pathBounds.left + pathBounds.width() / 2
+    val pathCenterY = pathBounds.top + pathBounds.height() / 2
+    val matrix = Matrix()
+    matrix.setScale(scaleFactor, scaleFactor)
+    matrix.preTranslate(-pathCenterX, -pathCenterY)
+    matrix.postTranslate(viewportSize / 2f, viewportSize / 2f)
+    path.transform(matrix)
+    return path.asComposePath()
+}
+
 @Composable
 internal fun PolygonComposableImpl(
     polygon: RoundedPolygon,
     modifier: Modifier = Modifier,
-    debug: Boolean = false
+    debug: Boolean = false,
+    stroked: Boolean = false
 ) {
     @Suppress("PrimitiveInCollection")
     val sizedShapes = remember(polygon) { mutableMapOf<Size, List<Cubic>>() }
@@ -117,32 +149,47 @@
                 // TODO: Can we use drawWithCache to simplify this?
                 drawContent()
                 val scale = min(size.width, size.height)
-                val shape = sizedShapes.getOrPut(size) { polygon.cubics.scaled(scale) }
                 if (debug) {
+                    val shape = sizedShapes.getOrPut(size) { polygon.cubics.scaled(scale) }
                     // Draw bounding boxes
                     val bounds = FloatArray(4)
                     polygon.calculateBounds(bounds = bounds)
-                    drawRect(Color.Green, topLeft = Offset(scale * bounds[0], scale * bounds[1]),
-                        size = Size(scale * (bounds[2] - bounds[0]),
-                            scale * (bounds[3] - bounds[1])),
-                        style = Stroke(2f))
+                    drawRect(
+                        Color.Green, topLeft = Offset(scale * bounds[0], scale * bounds[1]),
+                        size = Size(
+                            scale * (bounds[2] - bounds[0]),
+                            scale * (bounds[3] - bounds[1])
+                        ),
+                        style = Stroke(2f)
+                    )
                     polygon.calculateBounds(bounds = bounds, false)
-                    drawRect(Color.Yellow, topLeft = Offset(scale * bounds[0], scale * bounds[1]),
-                        size = Size(scale * (bounds[2] - bounds[0]),
-                            scale * (bounds[3] - bounds[1])),
-                        style = Stroke(2f))
+                    drawRect(
+                        Color.Yellow, topLeft = Offset(scale * bounds[0], scale * bounds[1]),
+                        size = Size(
+                            scale * (bounds[2] - bounds[0]),
+                            scale * (bounds[3] - bounds[1])
+                        ),
+                        style = Stroke(2f)
+                    )
                     polygon.calculateMaxBounds(bounds = bounds)
-                    drawRect(Color.Magenta, topLeft = Offset(scale * bounds[0], scale * bounds[1]),
-                        size = Size(scale * (bounds[2] - bounds[0]),
-                            scale * (bounds[3] - bounds[1])),
-                        style = Stroke(2f))
+                    drawRect(
+                        Color.Magenta, topLeft = Offset(scale * bounds[0], scale * bounds[1]),
+                        size = Size(
+                            scale * (bounds[2] - bounds[0]),
+                            scale * (bounds[3] - bounds[1])
+                        ),
+                        style = Stroke(2f)
+                    )
 
                     // Center of shape
                     drawCircle(Color.White, radius = 2f, center = center, style = Stroke(2f))
 
                     shape.forEach { cubic -> debugDraw(cubic) }
                 } else {
-                    drawPath(shape.toPath(), Color.White)
+                    val scaledPath = polygon.toPath()
+                    scaledPath.transform(scaleMatrix(scale, scale))
+                    val style = if (stroked) Stroke(size.width / 10f) else Fill
+                    drawPath(scaledPath.asComposePath(), Color.White, style = style)
                 }
             })
 }
@@ -297,6 +344,8 @@
 
     var debug by remember { mutableStateOf(false) }
 
+    var stroked by remember { mutableStateOf(false) }
+
     val morphed by remember {
         derivedStateOf {
             // NOTE: We need to access this variable to ensure we recalculate the morph !
@@ -340,16 +389,22 @@
                         )
                 ) {
                     // draw shape
-                    PolygonComposable(shape, Modifier.clickable { clickFn(shapeIx) })
+                    PolygonComposable(shape,
+                        Modifier.clickable { clickFn(shapeIx) },
+                        stroked = stroked
+                    )
                 }
             }
         }
         Row(horizontalArrangement = Arrangement.spacedBy(5.dp)) {
+            Button(onClick = onEditClicked) {
+                Text("Edit")
+            }
             Button(onClick = { debug = !debug }) {
                 Text(if (debug) "Debug" else "Shape")
             }
-            Button(onClick = onEditClicked) {
-                Text("Edit")
+            Button(onClick = { stroked = !stroked }) {
+                Text(if (stroked) "Fill" else "Stroke")
             }
         }
         Slider(value = progress.value.coerceIn(0f, 1f), onValueChange = {
@@ -363,7 +418,7 @@
                     interactionSource = remember { MutableInteractionSource() }
                 ) {
                     scope.launch { doAnimation(progress) }
-                }, debug)
+                }, debug, stroked)
     }
 }
 
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
index afac2f4..870961f 100644
--- 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
@@ -319,6 +319,7 @@
 fun ShapeEditor(params: ShapeParameters, output: (String) -> Unit, onClose: () -> Unit) {
     val shapeParams = params.selectedShape().value
     var debug by remember { mutableStateOf(false) }
+    var stroked by remember { mutableStateOf(false) }
     var autoSize by remember { mutableStateOf(true) }
 
     Column(
@@ -368,7 +369,7 @@
                 } else {
                     poly
                 }
-            }, debug = debug)
+            }, debug = debug, stroked = stroked)
         }
         Row {
             MyTextButton(
@@ -383,6 +384,11 @@
             )
             Spacer(Modifier.weight(1f))
             MyTextButton(
+                onClick = { stroked = !stroked },
+                text = if (stroked) "Fill" else "Stroke"
+            )
+            Spacer(Modifier.weight(1f))
+            MyTextButton(
                 onClick = { autoSize = !autoSize },
                 text = if (autoSize) "AutoSize" else "NoSizing"
             )
diff --git a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/MorphView.kt b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/MorphView.kt
index 4c226b0..b207a6c 100644
--- a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/MorphView.kt
+++ b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/MorphView.kt
@@ -77,6 +77,11 @@
     }
 
     override fun onDraw(canvas: Canvas) {
+        /*
+        // For debugging - it's good to check how stroking the paths looks occasionally
+        paint.style = Paint.Style.STROKE
+        paint.strokeWidth = width / 10f
+        */
         canvas.drawPath(path, paint)
     }
 }
diff --git a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt
index 61091ca..344d955 100644
--- a/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt
+++ b/graphics/integration-tests/testapp/src/main/java/androidx/graphics/shapes/test/ShapeView.kt
@@ -43,6 +43,11 @@
     }
 
     override fun onDraw(canvas: Canvas) {
+        /*
+        // For debugging - it's good to check how stroking the paths looks occasionally
+        paint.style = Paint.Style.STROKE
+        paint.strokeWidth = width / 10f
+        */
         canvas.drawPath(path, paint)
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/aggregate/AggregateMetric.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/aggregate/AggregateMetric.kt
index c1c5e9b..837e5bc 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/aggregate/AggregateMetric.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/aggregate/AggregateMetric.kt
@@ -122,19 +122,6 @@
                 aggregationField = fieldName,
             )
 
-        internal fun <R : Any> longMetric(
-            dataTypeName: String,
-            aggregationType: AggregationType,
-            fieldName: String,
-            mapper: (Long) -> R,
-        ): AggregateMetric<R> =
-            AggregateMetric(
-                converter = Converter.FromLong(mapper),
-                dataTypeName = dataTypeName,
-                aggregationType = aggregationType,
-                aggregationField = fieldName,
-            )
-
         /**
          * Creates a [AggregateMetric] returning sample or record counts. Internal for SDK use only.
          */
diff --git a/libraryversions.toml b/libraryversions.toml
index 5a1ee76..0841236 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -2,6 +2,7 @@
 ACTIVITY = "1.9.0-alpha02"
 ANNOTATION = "1.8.0-alpha01"
 ANNOTATION_EXPERIMENTAL = "1.4.0-rc01"
+ANNOTATION_REPLACEWITH = "1.0.0-alpha01"
 APPACTIONS_BUILTINTYPES = "1.0.0-alpha01"
 APPACTIONS_INTERACTION = "1.0.0-alpha01"
 APPCOMPAT = "1.7.0-alpha04"
@@ -24,7 +25,7 @@
 COLLECTION = "1.5.0-alpha01"
 COMPOSE = "1.7.0-alpha01"
 COMPOSE_COMPILER = "1.5.8"
-COMPOSE_MATERIAL3 = "1.2.0-rc01"
+COMPOSE_MATERIAL3 = "1.3.0-alpha01"
 COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha05"
 COMPOSE_MATERIAL3_ADAPTIVE_NAVIGATION_SUITE = "1.0.0-alpha02"
 COMPOSE_MATERIAL3_COMMON = "1.0.0-alpha01"
@@ -71,8 +72,8 @@
 GLANCE_WEAR_TILES = "1.0.0-alpha06"
 GRAPHICS_CORE = "1.0.0-beta01"
 GRAPHICS_FILTERS = "1.0.0-alpha01"
-GRAPHICS_PATH = "1.0.0-beta02"
-GRAPHICS_SHAPES = "1.0.0-alpha04"
+GRAPHICS_PATH = "1.0.0-rc01"
+GRAPHICS_SHAPES = "1.0.0-alpha05"
 GRIDLAYOUT = "1.1.0-beta02"
 HEALTH_CONNECT = "1.1.0-alpha07"
 HEALTH_SERVICES_CLIENT = "1.1.0-alpha02"
@@ -106,7 +107,7 @@
 PREFERENCE = "1.3.0-alpha01"
 PRINT = "1.1.0-beta01"
 PRIVACYSANDBOX_ACTIVITY = "1.0.0-alpha01"
-PRIVACYSANDBOX_ADS = "1.1.0-beta03"
+PRIVACYSANDBOX_ADS = "1.1.0-beta04"
 PRIVACYSANDBOX_PLUGINS = "1.0.0-alpha03"
 PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha12"
 PRIVACYSANDBOX_TOOLS = "1.0.0-alpha06"
diff --git a/lifecycle/lifecycle-runtime-compose/build.gradle b/lifecycle/lifecycle-runtime-compose/build.gradle
index 173eee3..e6d937c 100644
--- a/lifecycle/lifecycle-runtime-compose/build.gradle
+++ b/lifecycle/lifecycle-runtime-compose/build.gradle
@@ -34,7 +34,7 @@
 
 dependencies {
     api projectOrArtifact(":lifecycle:lifecycle-runtime-ktx")
-    api(projectOrArtifact(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
     api("androidx.compose.runtime:runtime:1.0.1")
     api("androidx.compose.ui:ui:1.0.1")
 
diff --git a/lifecycle/lifecycle-viewmodel-compose/build.gradle b/lifecycle/lifecycle-viewmodel-compose/build.gradle
index 404e68f..de2379f 100644
--- a/lifecycle/lifecycle-viewmodel-compose/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-compose/build.gradle
@@ -39,7 +39,7 @@
     api projectOrArtifact(":lifecycle:lifecycle-viewmodel-savedstate")
     api("androidx.compose.runtime:runtime:1.0.1")
     api "androidx.compose.ui:ui:1.0.1"
-    api(projectOrArtifact(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
 
     implementation(libs.kotlinStdlib)
 
diff --git a/lint-checks/integration-tests/build.gradle b/lint-checks/integration-tests/build.gradle
index 130e500..4028590 100644
--- a/lint-checks/integration-tests/build.gradle
+++ b/lint-checks/integration-tests/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     implementation(project(":annotation:annotation"))
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
     implementation(libs.kotlinStdlib)
 }
 
diff --git a/mediarouter/mediarouter/build.gradle b/mediarouter/mediarouter/build.gradle
index f408c28..1a1cade1 100644
--- a/mediarouter/mediarouter/build.gradle
+++ b/mediarouter/mediarouter/build.gradle
@@ -41,7 +41,10 @@
     implementation("androidx.palette:palette:1.0.0")
     implementation("androidx.recyclerview:recyclerview:1.1.0")
     implementation("androidx.appcompat:appcompat-resources:1.2.0")
-    implementation project(":annotation:annotation-experimental")
+
+    // TODO(b/307906685) Change to the stable version with the next release.
+    // See b/307906685#comment6 for details.
+    implementation("androidx.annotation:annotation-experimental:1.4.0-rc01")
 
     testImplementation(libs.junit)
     testImplementation(libs.testCore)
diff --git a/navigation/navigation-runtime/build.gradle b/navigation/navigation-runtime/build.gradle
index 443a46b..d83b630 100644
--- a/navigation/navigation-runtime/build.gradle
+++ b/navigation/navigation-runtime/build.gradle
@@ -35,7 +35,7 @@
     api("androidx.activity:activity-ktx:1.7.1")
     api("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
     api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
-    api(projectOrArtifact(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
     implementation('androidx.collection:collection:1.1.0')
 
     api(libs.kotlinStdlib)
diff --git a/navigation/navigation-ui/build.gradle b/navigation/navigation-ui/build.gradle
index eab9cd6..5843a18 100644
--- a/navigation/navigation-ui/build.gradle
+++ b/navigation/navigation-ui/build.gradle
@@ -47,7 +47,7 @@
     api("androidx.drawerlayout:drawerlayout:1.1.1")
     api("com.google.android.material:material:1.4.0")
     implementation("androidx.transition:transition:1.3.0")
-    api(projectOrArtifact(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
 
     androidTestImplementation(project(":internal-testutils-navigation"), {
         exclude group: "androidx.navigation", module: "navigation-common"
diff --git a/paging/samples/build.gradle b/paging/samples/build.gradle
index 861a0cd..b2ad700 100644
--- a/paging/samples/build.gradle
+++ b/paging/samples/build.gradle
@@ -41,7 +41,7 @@
     compileOnly(project(":annotation:annotation-sampled"))
 
     implementation("androidx.appcompat:appcompat:1.2.0")
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
     implementation("androidx.fragment:fragment-ktx:1.3.0")
     implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0")
     implementation("androidx.recyclerview:recyclerview:1.2.0")
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index 14c676d..b0fde86 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -26,5 +26,5 @@
 # Disable docs
 androidx.enableDocumentation=false
 androidx.playground.snapshotBuildId=11307821
-androidx.playground.metalavaBuildId=11319541
-androidx.studio.type=playground
+androidx.playground.metalavaBuildId=11328314
+androidx.studio.type=playground
\ No newline at end of file
diff --git a/privacysandbox/ads/ads-adservices-java/api/1.1.0-beta04.txt b/privacysandbox/ads/ads-adservices-java/api/1.1.0-beta04.txt
new file mode 100644
index 0000000..232abf5
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/1.1.0-beta04.txt
@@ -0,0 +1,99 @@
+// 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 @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataOutcome> getAdSelectionDataAsync(androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataRequest getAdSelectionDataRequest);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> persistAdSelectionResultAsync(androidx.privacysandbox.ads.adservices.adselection.PersistAdSelectionResultRequest persistAdSelectionResultRequest);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportEventAsync(androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest reportEventRequest);
+    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);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> updateAdCounterHistogramAsync(androidx.privacysandbox.ads.adservices.adselection.UpdateAdCounterHistogramRequest updateAdCounterHistogramRequest);
+    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 @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> fetchAndJoinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.FetchAndJoinCustomAudienceRequest request);
+    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 @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+  }
+
+  public static final class MeasurementManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+  public abstract class TopicsManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+  }
+
+  public static final class TopicsManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices-java/api/res-1.1.0-beta04.txt b/privacysandbox/ads/ads-adservices-java/api/res-1.1.0-beta04.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/res-1.1.0-beta04.txt
diff --git a/privacysandbox/ads/ads-adservices-java/api/restricted_1.1.0-beta04.txt b/privacysandbox/ads/ads-adservices-java/api/restricted_1.1.0-beta04.txt
new file mode 100644
index 0000000..232abf5
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/restricted_1.1.0-beta04.txt
@@ -0,0 +1,99 @@
+// 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 @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataOutcome> getAdSelectionDataAsync(androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataRequest getAdSelectionDataRequest);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> persistAdSelectionResultAsync(androidx.privacysandbox.ads.adservices.adselection.PersistAdSelectionResultRequest persistAdSelectionResultRequest);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportEventAsync(androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest reportEventRequest);
+    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);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> updateAdCounterHistogramAsync(androidx.privacysandbox.ads.adservices.adselection.UpdateAdCounterHistogramRequest updateAdCounterHistogramRequest);
+    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 @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> fetchAndJoinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.FetchAndJoinCustomAudienceRequest request);
+    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 @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+  }
+
+  public static final class MeasurementManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+  public abstract class TopicsManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+  }
+
+  public static final class TopicsManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/1.1.0-beta04.txt b/privacysandbox/ads/ads-adservices/api/1.1.0-beta04.txt
new file mode 100644
index 0000000..32149c9
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/1.1.0-beta04.txt
@@ -0,0 +1,514 @@
+// 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;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class AdSelectionFromOutcomesConfig {
+    ctor public AdSelectionFromOutcomesConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, java.util.List<java.lang.Long> adSelectionIds, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, android.net.Uri selectionLogicUri);
+    method public java.util.List<java.lang.Long> getAdSelectionIds();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+    method public android.net.Uri getSelectionLogicUri();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+    method public void setSelectionLogicUri(android.net.Uri);
+    property public final java.util.List<java.lang.Long> adSelectionIds;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+    property public final android.net.Uri selectionLogicUri;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+  }
+
+  public abstract class AdSelectionManager {
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? getAdSelectionData(androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataRequest getAdSelectionDataRequest, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataOutcome>);
+    method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? persistAdSelectionResult(androidx.privacysandbox.ads.adservices.adselection.PersistAdSelectionResultRequest persistAdSelectionResultRequest, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract suspend Object? reportEvent(androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest reportEventRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    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>);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract suspend Object? updateAdCounterHistogram(androidx.privacysandbox.ads.adservices.adselection.UpdateAdCounterHistogramRequest updateAdCounterHistogramRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    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();
+    method @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public boolean hasOutcome();
+    property public final long adSelectionId;
+    property public final android.net.Uri renderUri;
+    field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome.Companion Companion;
+    field @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome NO_OUTCOME;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public static final class AdSelectionOutcome.Companion {
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataOutcome {
+    ctor public GetAdSelectionDataOutcome(long adSelectionId, optional byte[]? adSelectionData);
+    method public byte[]? getAdSelectionData();
+    method public long getAdSelectionId();
+    property public final byte[]? adSelectionData;
+    property public final long adSelectionId;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataRequest {
+    ctor public GetAdSelectionDataRequest(optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller);
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? getSeller();
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class PersistAdSelectionResultRequest {
+    ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller, optional byte[]? adSelectionResult);
+    method public long getAdSelectionId();
+    method public byte[]? getAdSelectionResult();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? getSeller();
+    property public final long adSelectionId;
+    property public final byte[]? adSelectionResult;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class ReportEventRequest {
+    ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations, optional android.view.InputEvent? inputEvent);
+    method public long getAdSelectionId();
+    method public String getEventData();
+    method public String getEventKey();
+    method public android.view.InputEvent? getInputEvent();
+    method public int getReportingDestinations();
+    property public final long adSelectionId;
+    property public final String eventData;
+    property public final String eventKey;
+    property public final android.view.InputEvent? inputEvent;
+    property public final int reportingDestinations;
+    field public static final androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest.Companion Companion;
+    field public static final int FLAG_REPORTING_DESTINATION_BUYER = 2; // 0x2
+    field public static final int FLAG_REPORTING_DESTINATION_SELLER = 1; // 0x1
+  }
+
+  public static final class ReportEventRequest.Companion {
+  }
+
+  public final class ReportImpressionRequest {
+    ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public ReportImpressionRequest(long adSelectionId);
+    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;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class UpdateAdCounterHistogramRequest {
+    ctor public UpdateAdCounterHistogramRequest(long adSelectionId, int adEventType, androidx.privacysandbox.ads.adservices.common.AdTechIdentifier callerAdTech);
+    method public int getAdEventType();
+    method public long getAdSelectionId();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getCallerAdTech();
+    property public final int adEventType;
+    property public final long adSelectionId;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier callerAdTech;
+  }
+
+}
+
+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);
+    ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public AdData(android.net.Uri renderUri, String metadata, optional java.util.Set<java.lang.Integer> adCounterKeys, optional androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters);
+    ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public AdData(android.net.Uri renderUri, String metadata, optional java.util.Set<java.lang.Integer> adCounterKeys, optional androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters, optional String? adRenderId);
+    method public java.util.Set<java.lang.Integer> getAdCounterKeys();
+    method public androidx.privacysandbox.ads.adservices.common.AdFilters? getAdFilters();
+    method public String? getAdRenderId();
+    method public String getMetadata();
+    method public android.net.Uri getRenderUri();
+    property public final java.util.Set<java.lang.Integer> adCounterKeys;
+    property public final androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters;
+    property public final String? adRenderId;
+    property public final String metadata;
+    property public final android.net.Uri renderUri;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class AdFilters {
+    ctor public AdFilters(androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? frequencyCapFilters);
+    method public androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? getFrequencyCapFilters();
+    property public final androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? frequencyCapFilters;
+  }
+
+  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;
+  }
+
+  public sealed interface ExperimentalFeatures {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext10 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext10OptIn {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext8 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext8OptIn {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.RegisterSourceOptIn {
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class FrequencyCapFilters {
+    ctor public FrequencyCapFilters();
+    ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents);
+    ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents);
+    ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents);
+    ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForClickEvents);
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForClickEvents();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForImpressionEvents();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForViewEvents();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForWinEvents();
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForClickEvents;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents;
+    field public static final int AD_EVENT_TYPE_CLICK = 3; // 0x3
+    field public static final int AD_EVENT_TYPE_IMPRESSION = 1; // 0x1
+    field public static final int AD_EVENT_TYPE_VIEW = 2; // 0x2
+    field public static final int AD_EVENT_TYPE_WIN = 0; // 0x0
+    field public static final androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters.Companion Companion;
+  }
+
+  public static final class FrequencyCapFilters.Companion {
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class KeyedFrequencyCap {
+    ctor public KeyedFrequencyCap(int adCounterKey, int maxCount, java.time.Duration interval);
+    method public int getAdCounterKey();
+    method public java.time.Duration getInterval();
+    method public int getMaxCount();
+    property public final int adCounterKey;
+    property public final java.time.Duration interval;
+    property public final int maxCount;
+  }
+
+}
+
+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 @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? fetchAndJoinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.FetchAndJoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    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);
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class FetchAndJoinCustomAudienceRequest {
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals);
+    method public java.time.Instant? getActivationTime();
+    method public java.time.Instant? getExpirationTime();
+    method public android.net.Uri getFetchUri();
+    method public String? getName();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+    property public final java.time.Instant? activationTime;
+    property public final java.time.Instant? expirationTime;
+    property public final android.net.Uri fetchUri;
+    property public final String? name;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+  }
+
+  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.O) 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.O) 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 @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract suspend Object? registerSource(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request, 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);
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public final class SourceRegistrationRequest {
+    ctor public SourceRegistrationRequest(java.util.List<? extends android.net.Uri> registrationUris, optional android.view.InputEvent? inputEvent);
+    method public android.view.InputEvent? getInputEvent();
+    method public java.util.List<android.net.Uri> getRegistrationUris();
+    property public final android.view.InputEvent? inputEvent;
+    property public final java.util.List<android.net.Uri> registrationUris;
+  }
+
+  public static final class SourceRegistrationRequest.Builder {
+    ctor public SourceRegistrationRequest.Builder(java.util.List<? extends android.net.Uri> registrationUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+  }
+
+  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;
+  }
+
+  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);
+  }
+
+  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;
+  }
+
+  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 shouldRecordObservation();
+    property public final String adsSdkName;
+    property public final boolean shouldRecordObservation;
+  }
+
+  public static final class GetTopicsRequest.Builder {
+    ctor public GetTopicsRequest.Builder();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+  }
+
+  public final class GetTopicsResponse {
+    ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+    method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+  }
+
+  public final class Topic {
+    ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+    method public long getModelVersion();
+    method public long getTaxonomyVersion();
+    method public int getTopicId();
+    property public final long modelVersion;
+    property public final long taxonomyVersion;
+    property public final int topicId;
+  }
+
+  public abstract class TopicsManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+    method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+  }
+
+  public static final class TopicsManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/res-1.1.0-beta04.txt b/privacysandbox/ads/ads-adservices/api/res-1.1.0-beta04.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/res-1.1.0-beta04.txt
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta04.txt b/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta04.txt
new file mode 100644
index 0000000..32149c9
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta04.txt
@@ -0,0 +1,514 @@
+// 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;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class AdSelectionFromOutcomesConfig {
+    ctor public AdSelectionFromOutcomesConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, java.util.List<java.lang.Long> adSelectionIds, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, android.net.Uri selectionLogicUri);
+    method public java.util.List<java.lang.Long> getAdSelectionIds();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+    method public android.net.Uri getSelectionLogicUri();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+    method public void setSelectionLogicUri(android.net.Uri);
+    property public final java.util.List<java.lang.Long> adSelectionIds;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+    property public final android.net.Uri selectionLogicUri;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+  }
+
+  public abstract class AdSelectionManager {
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? getAdSelectionData(androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataRequest getAdSelectionDataRequest, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataOutcome>);
+    method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? persistAdSelectionResult(androidx.privacysandbox.ads.adservices.adselection.PersistAdSelectionResultRequest persistAdSelectionResultRequest, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract suspend Object? reportEvent(androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest reportEventRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    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>);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract suspend Object? updateAdCounterHistogram(androidx.privacysandbox.ads.adservices.adselection.UpdateAdCounterHistogramRequest updateAdCounterHistogramRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    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();
+    method @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public boolean hasOutcome();
+    property public final long adSelectionId;
+    property public final android.net.Uri renderUri;
+    field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome.Companion Companion;
+    field @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome NO_OUTCOME;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public static final class AdSelectionOutcome.Companion {
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataOutcome {
+    ctor public GetAdSelectionDataOutcome(long adSelectionId, optional byte[]? adSelectionData);
+    method public byte[]? getAdSelectionData();
+    method public long getAdSelectionId();
+    property public final byte[]? adSelectionData;
+    property public final long adSelectionId;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataRequest {
+    ctor public GetAdSelectionDataRequest(optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller);
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? getSeller();
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class PersistAdSelectionResultRequest {
+    ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller, optional byte[]? adSelectionResult);
+    method public long getAdSelectionId();
+    method public byte[]? getAdSelectionResult();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? getSeller();
+    property public final long adSelectionId;
+    property public final byte[]? adSelectionResult;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class ReportEventRequest {
+    ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations, optional android.view.InputEvent? inputEvent);
+    method public long getAdSelectionId();
+    method public String getEventData();
+    method public String getEventKey();
+    method public android.view.InputEvent? getInputEvent();
+    method public int getReportingDestinations();
+    property public final long adSelectionId;
+    property public final String eventData;
+    property public final String eventKey;
+    property public final android.view.InputEvent? inputEvent;
+    property public final int reportingDestinations;
+    field public static final androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest.Companion Companion;
+    field public static final int FLAG_REPORTING_DESTINATION_BUYER = 2; // 0x2
+    field public static final int FLAG_REPORTING_DESTINATION_SELLER = 1; // 0x1
+  }
+
+  public static final class ReportEventRequest.Companion {
+  }
+
+  public final class ReportImpressionRequest {
+    ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public ReportImpressionRequest(long adSelectionId);
+    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;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class UpdateAdCounterHistogramRequest {
+    ctor public UpdateAdCounterHistogramRequest(long adSelectionId, int adEventType, androidx.privacysandbox.ads.adservices.common.AdTechIdentifier callerAdTech);
+    method public int getAdEventType();
+    method public long getAdSelectionId();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getCallerAdTech();
+    property public final int adEventType;
+    property public final long adSelectionId;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier callerAdTech;
+  }
+
+}
+
+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);
+    ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public AdData(android.net.Uri renderUri, String metadata, optional java.util.Set<java.lang.Integer> adCounterKeys, optional androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters);
+    ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public AdData(android.net.Uri renderUri, String metadata, optional java.util.Set<java.lang.Integer> adCounterKeys, optional androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters, optional String? adRenderId);
+    method public java.util.Set<java.lang.Integer> getAdCounterKeys();
+    method public androidx.privacysandbox.ads.adservices.common.AdFilters? getAdFilters();
+    method public String? getAdRenderId();
+    method public String getMetadata();
+    method public android.net.Uri getRenderUri();
+    property public final java.util.Set<java.lang.Integer> adCounterKeys;
+    property public final androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters;
+    property public final String? adRenderId;
+    property public final String metadata;
+    property public final android.net.Uri renderUri;
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class AdFilters {
+    ctor public AdFilters(androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? frequencyCapFilters);
+    method public androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? getFrequencyCapFilters();
+    property public final androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? frequencyCapFilters;
+  }
+
+  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;
+  }
+
+  public sealed interface ExperimentalFeatures {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext10 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext10OptIn {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext8 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext8OptIn {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.RegisterSourceOptIn {
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class FrequencyCapFilters {
+    ctor public FrequencyCapFilters();
+    ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents);
+    ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents);
+    ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents);
+    ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForClickEvents);
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForClickEvents();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForImpressionEvents();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForViewEvents();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForWinEvents();
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForClickEvents;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents;
+    field public static final int AD_EVENT_TYPE_CLICK = 3; // 0x3
+    field public static final int AD_EVENT_TYPE_IMPRESSION = 1; // 0x1
+    field public static final int AD_EVENT_TYPE_VIEW = 2; // 0x2
+    field public static final int AD_EVENT_TYPE_WIN = 0; // 0x0
+    field public static final androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters.Companion Companion;
+  }
+
+  public static final class FrequencyCapFilters.Companion {
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class KeyedFrequencyCap {
+    ctor public KeyedFrequencyCap(int adCounterKey, int maxCount, java.time.Duration interval);
+    method public int getAdCounterKey();
+    method public java.time.Duration getInterval();
+    method public int getMaxCount();
+    property public final int adCounterKey;
+    property public final java.time.Duration interval;
+    property public final int maxCount;
+  }
+
+}
+
+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 @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? fetchAndJoinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.FetchAndJoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    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);
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class FetchAndJoinCustomAudienceRequest {
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals);
+    method public java.time.Instant? getActivationTime();
+    method public java.time.Instant? getExpirationTime();
+    method public android.net.Uri getFetchUri();
+    method public String? getName();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+    property public final java.time.Instant? activationTime;
+    property public final java.time.Instant? expirationTime;
+    property public final android.net.Uri fetchUri;
+    property public final String? name;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+  }
+
+  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.O) 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.O) 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 @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract suspend Object? registerSource(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request, 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);
+  }
+
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public final class SourceRegistrationRequest {
+    ctor public SourceRegistrationRequest(java.util.List<? extends android.net.Uri> registrationUris, optional android.view.InputEvent? inputEvent);
+    method public android.view.InputEvent? getInputEvent();
+    method public java.util.List<android.net.Uri> getRegistrationUris();
+    property public final android.view.InputEvent? inputEvent;
+    property public final java.util.List<android.net.Uri> registrationUris;
+  }
+
+  public static final class SourceRegistrationRequest.Builder {
+    ctor public SourceRegistrationRequest.Builder(java.util.List<? extends android.net.Uri> registrationUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+  }
+
+  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;
+  }
+
+  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);
+  }
+
+  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;
+  }
+
+  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 shouldRecordObservation();
+    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/tools/tools-apicompiler/build.gradle b/privacysandbox/tools/tools-apicompiler/build.gradle
index 19c3cec..670f655 100644
--- a/privacysandbox/tools/tools-apicompiler/build.gradle
+++ b/privacysandbox/tools/tools-apicompiler/build.gradle
@@ -44,6 +44,8 @@
 
     testImplementation(project(":privacysandbox:tools:tools-testing"))
     testImplementation(project(":room:room-compiler-processing-testing"))
+    testImplementationAarAsJar(project(":privacysandbox:activity:activity-core"))
+    testImplementationAarAsJar(project(":privacysandbox:activity:activity-provider"))
     testImplementationAarAsJar(project(":privacysandbox:ui:ui-core"))
     testImplementationAarAsJar(project(":privacysandbox:ui:ui-provider"))
     testImplementationAarAsJar(project(":privacysandbox:sdkruntime:sdkruntime-core"))
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/input/com/mysdk/MySdk.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/input/com/mysdk/MySdk.kt
index 7daabc5..154583e 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/input/com/mysdk/MySdk.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/input/com/mysdk/MySdk.kt
@@ -5,7 +5,7 @@
 import androidx.privacysandbox.tools.PrivacySandboxService
 import androidx.privacysandbox.tools.PrivacySandboxValue
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
 
 @PrivacySandboxService
 interface MySdk {
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/SdkActivityLauncherAndBinderWrapper.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/SdkActivityLauncherAndBinderWrapper.kt
index 45f4a43..7687057 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/SdkActivityLauncherAndBinderWrapper.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/SdkActivityLauncherAndBinderWrapper.kt
@@ -1,8 +1,8 @@
 package com.mysdk
 
 import android.os.Bundle
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
-import androidx.privacysandbox.ui.provider.SdkActivityLauncherFactory
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.provider.SdkActivityLauncherFactory
 
 public class SdkActivityLauncherAndBinderWrapper private constructor(
     private val `delegate`: SdkActivityLauncher,
diff --git a/privacysandbox/tools/tools-apigenerator/build.gradle b/privacysandbox/tools/tools-apigenerator/build.gradle
index 4975dd9..b461f26 100644
--- a/privacysandbox/tools/tools-apigenerator/build.gradle
+++ b/privacysandbox/tools/tools-apigenerator/build.gradle
@@ -48,6 +48,8 @@
     testImplementation(project(":privacysandbox:tools:tools-apipackager"))
     testImplementation(project(":privacysandbox:tools:tools-testing"))
     testImplementation(project(":room:room-compiler-processing-testing"))
+    testImplementationAarAsJar(project(":privacysandbox:activity:activity-core"))
+    testImplementationAarAsJar(project(":privacysandbox:activity:activity-client"))
     testImplementationAarAsJar(project(":privacysandbox:ui:ui-core"))
     testImplementationAarAsJar(project(":privacysandbox:ui:ui-client"))
     testImplementationAarAsJar(project(":privacysandbox:sdkruntime:sdkruntime-client"))
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 4270602..c5f5516 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
@@ -268,7 +268,7 @@
         val source = Source.kotlin(
             "com/mysdk/MySdk.kt", """
                     import androidx.privacysandbox.tools.PrivacySandboxService
-                    import androidx.privacysandbox.ui.core.SdkActivityLauncher
+                    import androidx.privacysandbox.activity.core.SdkActivityLauncher
                     @PrivacySandboxService
                     interface MySdk {
                         fun useLauncher(launcher: SdkActivityLauncher)
@@ -285,7 +285,7 @@
             "com/mysdk/MySdk.kt", """
                     import androidx.privacysandbox.tools.PrivacySandboxService
                     import androidx.privacysandbox.tools.PrivacySandboxInterface
-                    import androidx.privacysandbox.ui.core.SdkActivityLauncher
+                    import androidx.privacysandbox.activity.core.SdkActivityLauncher
                     @PrivacySandboxService
                     interface MySdk
 
@@ -305,7 +305,7 @@
             "com/mysdk/MySdk.kt", """
                     import androidx.privacysandbox.tools.PrivacySandboxService
                     import androidx.privacysandbox.tools.PrivacySandboxCallback
-                    import androidx.privacysandbox.ui.core.SdkActivityLauncher
+                    import androidx.privacysandbox.activity.core.SdkActivityLauncher
                     @PrivacySandboxService
                     interface MySdk
 
@@ -325,7 +325,7 @@
             "com/mysdk/MySdk.kt", """
                     import androidx.privacysandbox.tools.PrivacySandboxService
                     import androidx.privacysandbox.tools.PrivacySandboxValue
-                    import androidx.privacysandbox.ui.core.SdkActivityLauncher
+                    import androidx.privacysandbox.activity.core.SdkActivityLauncher
                     @PrivacySandboxService
                     interface MySdk
 
@@ -343,7 +343,7 @@
             "com/mysdk/MySdk.kt", """
                     import androidx.privacysandbox.tools.PrivacySandboxService
                     // Deliberate unused import
-                    import androidx.privacysandbox.ui.core.SdkActivityLauncher
+                    import androidx.privacysandbox.activity.core.SdkActivityLauncher
                     @PrivacySandboxService
                     interface MySdk {
                         fun doStuff(input: String)
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/input/com/sdkwithcallbacks/SdkService.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/input/com/sdkwithcallbacks/SdkService.kt
index ff49344..5c45597 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/input/com/sdkwithcallbacks/SdkService.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/input/com/sdkwithcallbacks/SdkService.kt
@@ -5,7 +5,7 @@
 import androidx.privacysandbox.tools.PrivacySandboxValue
 import androidx.privacysandbox.tools.PrivacySandboxInterface
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
 
 @PrivacySandboxService
 interface SdkService {
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkActivityLauncherProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkActivityLauncherProxy.kt
index b6407a6..40b91a3 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkActivityLauncherProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkActivityLauncherProxy.kt
@@ -2,10 +2,10 @@
 
 import android.os.Bundle
 import android.os.IBinder
-import androidx.privacysandbox.ui.client.toLauncherInfo
-import androidx.privacysandbox.ui.core.ISdkActivityLauncher
-import androidx.privacysandbox.ui.core.ISdkActivityLauncherCallback
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.client.toLauncherInfo
+import androidx.privacysandbox.activity.core.ISdkActivityLauncher
+import androidx.privacysandbox.activity.core.ISdkActivityLauncherCallback
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
 import kotlin.coroutines.resume
 import kotlin.coroutines.resumeWithException
 import kotlinx.coroutines.suspendCancellableCoroutine
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallback.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallback.kt
index 040ec55..4530541 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallback.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallback.kt
@@ -1,6 +1,6 @@
 package com.sdkwithcallbacks
 
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
 
 public interface SdkCallback {
     public fun onCompleteInterface(myInterface: MyInterface)
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/input/com/sdk/MySdk.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/input/com/sdk/MySdk.kt
index 413a47a..83a7c3f 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/input/com/sdk/MySdk.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/input/com/sdk/MySdk.kt
@@ -4,7 +4,7 @@
 import androidx.privacysandbox.tools.PrivacySandboxInterface
 import androidx.privacysandbox.tools.PrivacySandboxService
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
 
 @PrivacySandboxService
 interface MySdk {
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterface.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterface.kt
index 61b2aa6..6000042 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterface.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterface.kt
@@ -1,6 +1,6 @@
 package com.sdk
 
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
 
 public interface MyInterface {
     public suspend fun add(x: Int, y: Int): Int
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt
index ad696066..f8115b6 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt
@@ -1,6 +1,6 @@
 package com.sdk
 
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
 import com.sdk.PrivacySandboxThrowableParcelConverter.fromThrowableParcel
 import com.sdk.SdkActivityLauncherConverter.toBinder
 import kotlin.coroutines.resumeWithException
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/SdkActivityLauncherProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/SdkActivityLauncherProxy.kt
index 3f1becf..4efbba5 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/SdkActivityLauncherProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/SdkActivityLauncherProxy.kt
@@ -2,10 +2,10 @@
 
 import android.os.Bundle
 import android.os.IBinder
-import androidx.privacysandbox.ui.client.toLauncherInfo
-import androidx.privacysandbox.ui.core.ISdkActivityLauncher
-import androidx.privacysandbox.ui.core.ISdkActivityLauncherCallback
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.client.toLauncherInfo
+import androidx.privacysandbox.activity.core.ISdkActivityLauncher
+import androidx.privacysandbox.activity.core.ISdkActivityLauncherCallback
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
 import kotlin.coroutines.resume
 import kotlin.coroutines.resumeWithException
 import kotlinx.coroutines.suspendCancellableCoroutine
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/input/com/sdkwithvalues/SdkInterface.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/input/com/sdkwithvalues/SdkInterface.kt
index f6b969b..ad46445 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/input/com/sdkwithvalues/SdkInterface.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/input/com/sdkwithvalues/SdkInterface.kt
@@ -4,7 +4,7 @@
 import androidx.privacysandbox.tools.PrivacySandboxService
 import androidx.privacysandbox.tools.PrivacySandboxValue
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
 
 @PrivacySandboxService
 interface SdkInterface {
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkActivityLauncherProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkActivityLauncherProxy.kt
index ed632db..179853c 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkActivityLauncherProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkActivityLauncherProxy.kt
@@ -2,10 +2,10 @@
 
 import android.os.Bundle
 import android.os.IBinder
-import androidx.privacysandbox.ui.client.toLauncherInfo
-import androidx.privacysandbox.ui.core.ISdkActivityLauncher
-import androidx.privacysandbox.ui.core.ISdkActivityLauncherCallback
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.client.toLauncherInfo
+import androidx.privacysandbox.activity.core.ISdkActivityLauncher
+import androidx.privacysandbox.activity.core.ISdkActivityLauncherCallback
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
 import kotlin.coroutines.resume
 import kotlin.coroutines.resumeWithException
 import kotlinx.coroutines.suspendCancellableCoroutine
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkRequest.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkRequest.kt
index 31de0c3..65b2b06 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkRequest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkRequest.kt
@@ -1,6 +1,6 @@
 package com.sdkwithvalues
 
-import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.activity.core.SdkActivityLauncher
 
 public data class SdkRequest(
     public val id: Long,
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/SdkActivityLauncherProxyGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/SdkActivityLauncherProxyGenerator.kt
index 840d30c..fa465ff 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/SdkActivityLauncherProxyGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/SdkActivityLauncherProxyGenerator.kt
@@ -38,7 +38,7 @@
         const val proxyClassName = "SdkActivityLauncherProxy"
         const val converterClassName = "SdkActivityLauncherConverter"
         val iSdkActivityLauncher = ClassName(
-            "androidx.privacysandbox.ui.core",
+            "androidx.privacysandbox.activity.core",
             "ISdkActivityLauncher"
         )
     }
@@ -68,7 +68,7 @@
 
     private fun launchSdkActivityFunSpec() = FunSpec.builder("launchSdkActivity").build {
         val transactionCallbackName = ClassName(
-            "androidx.privacysandbox.ui.core",
+            "androidx.privacysandbox.activity.core",
             "ISdkActivityLauncherCallback",
             "Stub"
         )
@@ -140,7 +140,7 @@
                 addStatement(
                     "return launcher.%M()",
                     MemberName(
-                        "androidx.privacysandbox.ui.client",
+                        "androidx.privacysandbox.activity.client",
                         "toLauncherInfo"
                     )
                 )
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/SdkActivityLauncherWrapperGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/SdkActivityLauncherWrapperGenerator.kt
index 37aa3da..e096a86 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/SdkActivityLauncherWrapperGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/SdkActivityLauncherWrapperGenerator.kt
@@ -68,7 +68,7 @@
             CodeBlock.of(
                 "%T.fromLauncherInfo(launcherInfo)",
                 ClassName(
-                    "androidx.privacysandbox.ui.provider",
+                    "androidx.privacysandbox.activity.provider",
                     "SdkActivityLauncherFactory"
                 ),
             ),
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/model/Models.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/model/Models.kt
index afc79843..eb3f5f4 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/model/Models.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/model/Models.kt
@@ -65,8 +65,10 @@
     val any = Type("kotlin", simpleName = "Any")
     val sandboxedUiAdapter =
         Type(packageName = "androidx.privacysandbox.ui.core", simpleName = "SandboxedUiAdapter")
-    val sdkActivityLauncher =
-        Type(packageName = "androidx.privacysandbox.ui.core", simpleName = "SdkActivityLauncher")
+    val sdkActivityLauncher = Type(
+        packageName = "androidx.privacysandbox.activity.core",
+        simpleName = "SdkActivityLauncher"
+    )
 
     fun list(elementType: Type) = Type(
         packageName = "kotlin.collections",
diff --git a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
index 40089ca..777c33f 100644
--- a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
+++ b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
@@ -117,20 +117,8 @@
     }
 
     private fun getRawErrorMessages(): List<DiagnosticMessage> {
-        // Filter SdkActivityLauncher deprecation warnings. Our tests currently use the old version
-        // so the warnings are expected.
-        // TODO(b/307696996) - Remove this once the generator uses the new version exclusively.
-        val warningsToIgnore = listOf(
-            "'SdkActivityLauncher' is deprecated.",
-            "'SdkActivityLauncherFactory' is deprecated.",
-            "'toLauncherInfo(): Bundle' is deprecated.",
-        )
-        val filteredWarnings = result.diagnostics[Diagnostic.Kind.WARNING]?.filter { warning ->
-            !warningsToIgnore.any { warning.msg.contains(it) }
-        } ?: emptyList()
-
         return (result.diagnostics[Diagnostic.Kind.ERROR] ?: emptyList()) +
-            filteredWarnings +
+            (result.diagnostics[Diagnostic.Kind.WARNING] ?: emptyList()) +
             (result.diagnostics[Diagnostic.Kind.MANDATORY_WARNING] ?: emptyList())
     }
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt
index a5e9839..db561a7 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt
@@ -164,8 +164,8 @@
             field.declaration.wrapAsOriginatingElement()
         }
         is KspMemberContainer -> {
-            declaration?.wrapAsOriginatingElement()
+            declaration!!.wrapAsOriginatingElement()
         }
-        else -> null
-    } ?: error("Originating element is not implemented for ${this.javaClass}")
+        else -> error("Originating element is not implemented for ${this.javaClass}")
+    }
 }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt
index 20f735b..bc34971 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt
@@ -37,12 +37,11 @@
  * Return a reference to the containing file or class declaration via a wrapper that implements the
  * [javax.lang.model.element.Element] API so that we can report it to JavaPoet.
  */
-internal fun KSAnnotated.wrapAsOriginatingElement(): OriginatingElementWrapper? {
-    val ksDeclaration = this as? KSDeclaration ?: return null
-
+internal fun KSAnnotated.wrapAsOriginatingElement(): OriginatingElementWrapper {
+    val ksDeclaration = this as KSDeclaration
+    // Use the source file as originating element if the KSAnnotated is from a source file, and use
+    // the class declaration if it's from a compiled class file.
     return ksDeclaration.containingFile?.let {
         KSFileAsOriginatingElement(it)
-    } ?: (ksDeclaration as? KSClassDeclaration)?.let {
-        KSClassDeclarationAsOriginatingElement(it)
-    }
+    } ?: KSClassDeclarationAsOriginatingElement(ksDeclaration as KSClassDeclaration)
 }
diff --git a/room/room-compiler/build.gradle b/room/room-compiler/build.gradle
index f0c0121..d77a654 100644
--- a/room/room-compiler/build.gradle
+++ b/room/room-compiler/build.gradle
@@ -296,7 +296,7 @@
 
 tasks.withType(Test).configureEach {
     it.systemProperty("androidx.room.compiler.processing.strict", "true")
-    it.maxParallelForks(5)
+    it.maxParallelForks(8)
     if (project.providers.environmentVariable("GITHUB_ACTIONS").present) {
         // limit memory usage to avoid running out of memory in the docker container.
         it.maxHeapSize("512m")
diff --git a/room/room-runtime/api/current.txt b/room/room-runtime/api/current.txt
index 17b1029..bd860e33 100644
--- a/room/room-runtime/api/current.txt
+++ b/room/room-runtime/api/current.txt
@@ -70,7 +70,6 @@
     method public void close();
     method public androidx.sqlite.db.SupportSQLiteStatement compileStatement(String sql);
     method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
-    method protected androidx.room.RoomOpenDelegateMarker createOpenDelegate();
     method @Deprecated protected androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration config);
     method @Deprecated public void endTransaction();
     method public androidx.room.InvalidationTracker getInvalidationTracker();
@@ -81,8 +80,7 @@
     method @Deprecated public <T> T? getTypeConverter(Class<T> klass);
     method public boolean inTransaction();
     method @CallSuper public void init(androidx.room.DatabaseConfiguration configuration);
-    method protected void internalInitInvalidationTracker(androidx.sqlite.db.SupportSQLiteDatabase db);
-    method protected void internalInitInvalidationTracker(androidx.sqlite.SQLiteConnection connection);
+    method @Deprecated protected void internalInitInvalidationTracker(androidx.sqlite.db.SupportSQLiteDatabase db);
     method public boolean isOpen();
     method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery query);
     method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery query, optional android.os.CancellationSignal? signal);
diff --git a/room/room-runtime/api/restricted_current.txt b/room/room-runtime/api/restricted_current.txt
index feeae86..ae0bc5e 100644
--- a/room/room-runtime/api/restricted_current.txt
+++ b/room/room-runtime/api/restricted_current.txt
@@ -138,7 +138,6 @@
     method public void close();
     method public androidx.sqlite.db.SupportSQLiteStatement compileStatement(String sql);
     method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
-    method protected androidx.room.RoomOpenDelegateMarker createOpenDelegate();
     method @Deprecated protected androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration config);
     method @Deprecated public void endTransaction();
     method public androidx.room.InvalidationTracker getInvalidationTracker();
@@ -150,8 +149,7 @@
     method @Deprecated public <T> T? getTypeConverter(Class<T> klass);
     method public boolean inTransaction();
     method @CallSuper public void init(androidx.room.DatabaseConfiguration configuration);
-    method protected void internalInitInvalidationTracker(androidx.sqlite.db.SupportSQLiteDatabase db);
-    method protected void internalInitInvalidationTracker(androidx.sqlite.SQLiteConnection connection);
+    method @Deprecated protected void internalInitInvalidationTracker(androidx.sqlite.db.SupportSQLiteDatabase db);
     method public boolean isOpen();
     method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery query);
     method public android.database.Cursor query(androidx.sqlite.db.SupportSQLiteQuery query, optional android.os.CancellationSignal? signal);
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
index d1feedf..5158b3d 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
@@ -356,6 +356,7 @@
      * @return A new delegate to be used while opening the database
      * @throws NotImplementedError by default
      */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     protected actual open fun createOpenDelegate(): RoomOpenDelegateMarker {
         throw NotImplementedError()
     }
@@ -674,6 +675,7 @@
      *
      * @param db The database instance.
      */
+    @Deprecated("No longer called by generated")
     protected open fun internalInitInvalidationTracker(db: SupportSQLiteDatabase) {
         invalidationTracker.internalInit(db)
     }
@@ -685,8 +687,10 @@
      *
      * @param connection The database connection.
      */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     protected open fun internalInitInvalidationTracker(connection: SQLiteConnection) {
         if (connection is SupportSQLiteConnection) {
+            @Suppress("DEPRECATION")
             internalInitInvalidationTracker(connection.db)
         } else {
             TODO("Not yet migrated to use SQLiteDriver")
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
index 98a4526..539c5bf 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
@@ -58,6 +58,7 @@
      *
      * @return A new delegate to be used while opening the database
      */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     protected open fun createOpenDelegate(): RoomOpenDelegateMarker
 
     /**
diff --git a/room/room-runtime/src/jvmMain/kotlin/androidx/room/RoomDatabase.jvm.kt b/room/room-runtime/src/jvmMain/kotlin/androidx/room/RoomDatabase.jvm.kt
index 125c3e2..37216e0 100644
--- a/room/room-runtime/src/jvmMain/kotlin/androidx/room/RoomDatabase.jvm.kt
+++ b/room/room-runtime/src/jvmMain/kotlin/androidx/room/RoomDatabase.jvm.kt
@@ -72,6 +72,7 @@
      * @return A new delegate to be used while opening the database
      * @throws NotImplementedError by default
      */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     protected actual open fun createOpenDelegate(): RoomOpenDelegateMarker {
         throw NotImplementedError()
     }
diff --git a/room/room-runtime/src/nativeMain/kotlin/androidx/room/RoomDatabase.native.kt b/room/room-runtime/src/nativeMain/kotlin/androidx/room/RoomDatabase.native.kt
index 787a842..ae9ee7d 100644
--- a/room/room-runtime/src/nativeMain/kotlin/androidx/room/RoomDatabase.native.kt
+++ b/room/room-runtime/src/nativeMain/kotlin/androidx/room/RoomDatabase.native.kt
@@ -72,6 +72,7 @@
      * @return A new delegate to be used while opening the database
      * @throws NotImplementedError by default
      */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     protected actual open fun createOpenDelegate(): RoomOpenDelegateMarker {
         throw NotImplementedError()
     }
diff --git a/settings.gradle b/settings.gradle
index 3f53728..4235ec6 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -340,6 +340,9 @@
 includeProject(":annotation:annotation-experimental")
 includeProject(":annotation:annotation-experimental-lint")
 includeProject(":annotation:annotation-experimental-lint-integration-tests", "annotation/annotation-experimental-lint/integration-tests")
+includeProject(":annotation:annotation-replacewith")
+includeProject(":annotation:annotation-replacewith-lint")
+includeProject(":annotation:annotation-replacewith-lint-integration-tests", "annotation/annotation-replacewith-lint/integration-tests")
 includeProject(":annotation:annotation-sampled")
 includeProject(":appactions:builtintypes:builtintypes", [BuildType.MAIN])
 includeProject(":appactions:builtintypes:builtintypes:builtintypes-samples", "appactions/builtintypes/builtintypes/samples", [BuildType.MAIN])
diff --git a/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt b/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt
index 791f50c..8f89ece7 100644
--- a/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt
+++ b/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt
@@ -20,6 +20,10 @@
 import androidx.sqlite.SQLiteConnection
 import androidx.sqlite.SQLiteDriver
 
+/**
+ * A [SQLiteDriver] that uses a bundled version of SQLite included as a native component of the
+ * library.
+ */
 // TODO(b/313895287): Explore usability of @FastNative and @CriticalNative for the external functions.
 actual class BundledSQLiteDriver actual constructor(
     private val filename: String
diff --git a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.kt b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.kt
index 82f35e8..e28e75b36 100644
--- a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.kt
+++ b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.kt
@@ -16,6 +16,9 @@
 
 package androidx.sqlite.driver.bundled
 
+import androidx.annotation.RestrictTo
 import androidx.sqlite.SQLiteConnection
 
+// Restricted instead of internal due to KT-37316
+@RestrictTo(RestrictTo.Scope.LIBRARY)
 expect class BundledSQLiteConnection : SQLiteConnection
diff --git a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.kt b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.kt
index e7e0625..b13f846 100644
--- a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.kt
+++ b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.kt
@@ -18,4 +18,8 @@
 
 import androidx.sqlite.SQLiteDriver
 
+/**
+ * A [SQLiteDriver] that uses a bundled version of SQLite included as a native component of this
+ * library.
+ */
 expect class BundledSQLiteDriver(filename: String) : SQLiteDriver
diff --git a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.kt b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.kt
index 85071f1..c572dc6 100644
--- a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.kt
+++ b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.kt
@@ -16,6 +16,9 @@
 
 package androidx.sqlite.driver.bundled
 
+import androidx.annotation.RestrictTo
 import androidx.sqlite.SQLiteStatement
 
+// Restricted instead of internal due to KT-37316
+@RestrictTo(RestrictTo.Scope.LIBRARY)
 expect class BundledSQLiteStatement : SQLiteStatement
diff --git a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.nativeCommon.kt b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.nativeCommon.kt
index 50a7940..ad97d05 100644
--- a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.nativeCommon.kt
+++ b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.nativeCommon.kt
@@ -16,5 +16,11 @@
 
 package androidx.sqlite.driver.bundled
 
+import androidx.sqlite.SQLiteDriver
+
+/**
+ * A [SQLiteDriver] that uses a bundled version of SQLite included as a native component of this
+ * library.
+ */
 actual typealias BundledSQLiteDriver =
     androidx.sqlite.driver.NativeSQLiteDriver
diff --git a/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteDriver.android.kt b/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteDriver.android.kt
index 34616c7..9e4e28e 100644
--- a/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteDriver.android.kt
+++ b/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteDriver.android.kt
@@ -20,6 +20,10 @@
 import androidx.sqlite.SQLiteConnection
 import androidx.sqlite.SQLiteDriver
 
+/**
+ * A [SQLiteDriver] implemented by [android.database] and that uses the Android's SDK SQLite
+ * APIs.
+ */
 class AndroidSQLiteDriver(
     private val filename: String
 ) : SQLiteDriver {
diff --git a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteDriver.kt b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteDriver.kt
index f8a7232..e9efb3e 100644
--- a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteDriver.kt
+++ b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteDriver.kt
@@ -30,10 +30,13 @@
 import sqlite3.sqlite3_open_v2
 
 /**
- * TODO:
- *  * (b/307917398) more open flags
- *  * (b/304295573) busy handler registering
+ * A [SQLiteDriver] that uses a version of SQLite included with the host operating system.
+ *
+ * Usage of this driver expects that `libsqlite` can be found in the shared library path.
  */
+// TODO:
+//    (b/307917398) more open flags
+//    (b/304295573) busy handler registering
 @OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
 class NativeSQLiteDriver(
     private val filename: String
diff --git a/core/core/src/main/java/androidx/core/util/Supplier.java b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/Invalidation.java
similarity index 65%
copy from core/core/src/main/java/androidx/core/util/Supplier.java
copy to sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/Invalidation.java
index 9deba96..62ae509 100644
--- a/core/core/src/main/java/androidx/core/util/Supplier.java
+++ b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/Invalidation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,11 @@
  * limitations under the License.
  */
 
-package androidx.core.util;
+package androidx.sqlite.inspection;
 
 /**
- * Compat version of {@link java.util.function.Supplier}
- * @param <T> the type of the input to the operation
+ * Triggers invalidation after SQL mutations are executed
  */
-public interface Supplier<T> {
-
-    /**
-     * Gets a result.
-     *
-     * @return a result
-     */
-    T get();
+interface Invalidation {
+    void triggerInvalidations();
 }
diff --git a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/RoomInvalidationRegistry.java b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/RoomInvalidationRegistry.java
index 44def67..566c732 100644
--- a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/RoomInvalidationRegistry.java
+++ b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/RoomInvalidationRegistry.java
@@ -36,7 +36,7 @@
  * The list of instances of InvalidationTrackers are cached to avoid re-finding them after each
  * query. Make sure to call {@link #invalidateCache()} after a new database connection is detected.
  */
-class RoomInvalidationRegistry {
+class RoomInvalidationRegistry implements Invalidation {
     private static final String TAG = "RoomInvalidationRegistry";
     private static final String INVALIDATION_TRACKER_QNAME = "androidx.room.InvalidationTracker";
 
@@ -64,7 +64,8 @@
      * <p>
      * If the list of InvalidationTracker instances are not cached, this will do a lookup.
      */
-    void triggerInvalidations() {
+    @Override
+    public void triggerInvalidations() {
         if (mInvoker == null) {
             return;
         }
diff --git a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqlDelight2Invalidation.java b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqlDelight2Invalidation.java
new file mode 100644
index 0000000..5598d1a
--- /dev/null
+++ b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqlDelight2Invalidation.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.sqlite.inspection;
+
+import android.annotation.SuppressLint;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.inspection.ArtTooling;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * An [Invalidation] for the SqlDelight 2 library.
+ * <p>
+ * SqlDelight 2 invalidation API uses an internal "queryKey" to associate queries with listeners.
+ * The key is created by the generated code and is typically just the affected table name but can
+ * in theory be anything. In fact, a user can register a listener directly using
+ * SqlDriver#addListener() and provide their own queryKeys. This will work as long as the user
+ * also manages notification using SqlDriver#notifyListeners().
+ * <p>
+ * The public API that notifies listeners requires this queryKey:
+ * <pre>
+ *   override fun notifyListeners(vararg queryKeys: String)
+ * </pre>
+ * There is no public API that works without it and there is no public API that lists the current
+ * listeners or queryKey's.
+ * <p>
+ * Because of this, we need to access the private field AndroidSqliteDriver#listeners and extract
+ * the registered queryKeys.
+ */
+class SqlDelight2Invalidation implements Invalidation {
+    public static final String TAG = "StudioInspectors";
+    public static final String HIDDEN_TAG = "studio.inspectors";
+
+    public static final String DRIVER_CLASSNAME =
+            "app.cash.sqldelight.driver.android.AndroidSqliteDriver";
+    public static final String NOTIFY_METHOD = "notifyListeners";
+    public static final String LISTENERS_FIELD = "listeners";
+
+    private final @NonNull ArtTooling mArtTooling;
+    private final @NonNull Class<?> mDriverClass;
+    private final @NonNull Method mNotifyListenersMethod;
+    private final @NonNull Field mListenersField;
+
+    static Invalidation create(@NonNull ArtTooling artTooling) {
+        try {
+            ClassLoader classLoader = Objects.requireNonNull(
+                    SqlDelight2Invalidation.class.getClassLoader());
+            Class<?> driverClass = classLoader.loadClass(DRIVER_CLASSNAME);
+            Method notifyListenersMethod =
+                    driverClass.getDeclaredMethod(NOTIFY_METHOD, String[].class);
+            Field listenersField = driverClass.getDeclaredField(LISTENERS_FIELD);
+            listenersField.setAccessible(true);
+            return new SqlDelight2Invalidation(
+                    artTooling,
+                    driverClass,
+                    notifyListenersMethod,
+                    listenersField);
+        } catch (ClassNotFoundException e) {
+            Log.v(HIDDEN_TAG, "SqlDelight 2 not found", e);
+            return () -> {
+            };
+        } catch (Exception e) {
+            Log.w(TAG, "Error setting up SqlDelight 2 invalidation", e);
+            return () -> {
+            };
+        }
+    }
+
+    private SqlDelight2Invalidation(
+            @NonNull ArtTooling artTooling,
+            @NonNull Class<?> driverClass,
+            @NonNull Method notifyListenersMethod,
+            @NonNull Field listenersField) {
+        mArtTooling = artTooling;
+        mDriverClass = driverClass;
+        mNotifyListenersMethod = notifyListenersMethod;
+        mListenersField = listenersField;
+    }
+
+    @SuppressLint("BanUncheckedReflection")
+    @Override
+    public void triggerInvalidations() {
+        for (Object driver : mArtTooling.findInstances(mDriverClass)) {
+            try {
+                @SuppressWarnings("unchecked")
+                Map<String, Object> listeners =
+                        Objects.requireNonNull((Map<String, Object>) mListenersField.get(driver));
+                synchronized (listeners) {
+                    mNotifyListenersMethod.invoke(
+                            driver,
+                            (Object) listeners.keySet().toArray(new String[0]));
+                }
+            } catch (Exception e) {
+                Log.w(TAG, "Error invalidating SqlDriver", e);
+            }
+        }
+    }
+}
diff --git a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqlDelightInvalidation.java b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqlDelightInvalidation.java
index cca016b..e37adfe 100644
--- a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqlDelightInvalidation.java
+++ b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqlDelightInvalidation.java
@@ -17,62 +17,62 @@
 package androidx.sqlite.inspection;
 
 import android.annotation.SuppressLint;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
-import androidx.inspection.InspectorEnvironment;
+import androidx.inspection.ArtTooling;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.util.List;
 import java.util.Objects;
 
-class SqlDelightInvalidation {
+class SqlDelightInvalidation implements Invalidation {
+    public static final String TAG = "StudioInspectors";
+    public static final String HIDDEN_TAG = "studio.inspectors";
+
     private static final String SQLDELIGHT_QUERY_CLASS_NAME = "com.squareup.sqldelight.Query";
     private static final String SQLDELIGHT_NOTIFY_METHOD_NAME = "notifyDataChanged";
 
-    private final InspectorEnvironment mEnvironment;
-    private final Class<?> mQueryClass;
-    private final Method mNotifyDataChangeMethod;
-
-    SqlDelightInvalidation(InspectorEnvironment environment, Class<?> queryClass,
-            Method notifyDataChangeMethod) {
-        mQueryClass = queryClass;
-        mEnvironment = environment;
-        mNotifyDataChangeMethod = notifyDataChangeMethod;
-    }
+    private final @NonNull ArtTooling mArtTooling;
+    private final @NonNull Class<?> mQueryClass;
+    private final @NonNull Method mNotifyDataChangeMethod;
 
     @NonNull
-    static SqlDelightInvalidation create(@NonNull InspectorEnvironment environment) {
+    static Invalidation create(@NonNull ArtTooling artTooling) {
         ClassLoader classLoader = SqlDelightInvalidation.class.getClassLoader();
         Objects.requireNonNull(classLoader);
         try {
             Class<?> queryClass = classLoader.loadClass(SQLDELIGHT_QUERY_CLASS_NAME);
             Method notifyMethod = queryClass.getMethod(SQLDELIGHT_NOTIFY_METHOD_NAME);
-            return new SqlDelightInvalidation(environment, queryClass, notifyMethod);
-        } catch (ClassNotFoundException | NoSuchMethodException e) {
-            return new SqlDelightInvalidation(environment, null, null);
+            return new SqlDelightInvalidation(artTooling, queryClass, notifyMethod);
+        } catch (ClassNotFoundException e) {
+            Log.v(HIDDEN_TAG, "SqlDelight not found", e);
+            return () -> {
+            };
+        } catch (Exception e) {
+            Log.w(TAG, "Error setting up SqlDelight invalidation", e);
+            return () -> {
+            };
         }
     }
 
-    void triggerInvalidations() {
-        if (mQueryClass == null || mNotifyDataChangeMethod == null) {
-            return;
-        }
+    private SqlDelightInvalidation(@NonNull ArtTooling artTooling, @NonNull Class<?> queryClass,
+            @NonNull Method notifyDataChangeMethod) {
+        mArtTooling = artTooling;
+        mQueryClass = queryClass;
+        mNotifyDataChangeMethod = notifyDataChangeMethod;
+    }
+
+    @SuppressLint("BanUncheckedReflection")
+    @Override
+    public void triggerInvalidations() {
         // invalidating all queries because we can't say which ones were actually affected.
-        List<?> queries = mEnvironment.artTooling().findInstances(mQueryClass);
-        for (Object query: queries) {
-            notifyDataChanged(query);
-        }
-    }
-
-    @SuppressLint("BanUncheckedReflection") // Not a platform method.
-    private void notifyDataChanged(Object query) {
-        try {
-            mNotifyDataChangeMethod.invoke(query);
-        } catch (IllegalAccessException | InvocationTargetException e) {
-            // ok it didn't work out for us,
-            // in first version we don't have a special UI around it,
-            // so we can't do much about it.
+        for (Object query: mArtTooling.findInstances(mQueryClass)) {
+            try {
+                mNotifyDataChangeMethod.invoke(query);
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                Log.w(TAG, "Error calling notifyDataChanged", e);
+            }
         }
     }
 }
diff --git a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java
index 06febd8..c29c45f 100644
--- a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java
+++ b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java
@@ -182,15 +182,16 @@
      */
     private final RoomInvalidationRegistry mRoomInvalidationRegistry;
 
-    @NonNull
-    private final SqlDelightInvalidation mSqlDelightInvalidation;
+    private final List<Invalidation> mInvalidations = new ArrayList<>();
 
     SqliteInspector(@NonNull Connection connection, @NonNull InspectorEnvironment environment) {
         super(connection);
         mEnvironment = environment;
         mIOExecutor = environment.executors().io();
         mRoomInvalidationRegistry = new RoomInvalidationRegistry(mEnvironment);
-        mSqlDelightInvalidation = SqlDelightInvalidation.create(mEnvironment);
+        mInvalidations.add(mRoomInvalidationRegistry);
+        mInvalidations.add(SqlDelightInvalidation.create(mEnvironment.artTooling()));
+        mInvalidations.add(SqlDelight2Invalidation.create(mEnvironment.artTooling()));
 
         mDatabaseRegistry = new DatabaseRegistry(
                 new DatabaseRegistry.Callback() {
@@ -669,8 +670,9 @@
 
     private void triggerInvalidation(String query) {
         if (getSqlStatementType(query) != DatabaseUtils.STATEMENT_SELECT) {
-            mSqlDelightInvalidation.triggerInvalidations();
-            mRoomInvalidationRegistry.triggerInvalidations();
+            for (Invalidation invalidation : mInvalidations) {
+                invalidation.triggerInvalidations();
+            }
         }
     }
 
diff --git a/testutils/testutils-appcompat/build.gradle b/testutils/testutils-appcompat/build.gradle
index a66bd45..eb47b7a 100644
--- a/testutils/testutils-appcompat/build.gradle
+++ b/testutils/testutils-appcompat/build.gradle
@@ -32,7 +32,7 @@
 dependencies {
     api(project(":internal-testutils-runtime"))
     api(project(":appcompat:appcompat"))
-    api(project(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
 
     implementation(libs.testExtJunit)
     implementation(libs.testCore)
diff --git a/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
index 58fce87..728ffaf 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
+++ b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
@@ -1059,7 +1059,7 @@
         var animatedFraction = -1f
         var animatedMillis = -1L
         val removeListener = object : Consumer<TransitionSeekController> {
-            override fun accept(t: TransitionSeekController?) {
+            override fun accept(value: TransitionSeekController) {
                 seekController.removeOnProgressChangedListener(this)
             }
         }
diff --git a/tv/tv-material/api/current.txt b/tv/tv-material/api/current.txt
index 3837a8d..64ec25f 100644
--- a/tv/tv-material/api/current.txt
+++ b/tv/tv-material/api/current.txt
@@ -230,7 +230,7 @@
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ClickableSurfaceShape {
   }
 
-  @SuppressCompatibility @androidx.compose.runtime.Stable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ColorScheme {
+  @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 border, long borderVariant, long scrim);
     method public androidx.tv.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 border, optional long borderVariant, optional long scrim);
     method public long getBackground();
@@ -294,16 +294,16 @@
   }
 
   public final class ColorSchemeKt {
-    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static long contentColorFor(androidx.tv.material3.ColorScheme, long backgroundColor);
+    method public static long contentColorFor(androidx.tv.material3.ColorScheme, long backgroundColor);
     method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public static long contentColorFor(long backgroundColor);
-    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.tv.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 border, optional long borderVariant, optional long scrim);
-    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.tv.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 border, optional long borderVariant, optional long scrim);
-    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static long surfaceColorAtElevation(androidx.tv.material3.ColorScheme, float elevation);
+    method public static androidx.tv.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 border, optional long borderVariant, optional long scrim);
+    method public static androidx.tv.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 border, optional long borderVariant, optional long scrim);
+    method public static long surfaceColorAtElevation(androidx.tv.material3.ColorScheme, float elevation);
   }
 
   public final class ContentColorKt {
-    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
-    property @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
+    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;
   }
 
   @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class DrawerState {
diff --git a/tv/tv-material/api/restricted_current.txt b/tv/tv-material/api/restricted_current.txt
index 3837a8d..64ec25f 100644
--- a/tv/tv-material/api/restricted_current.txt
+++ b/tv/tv-material/api/restricted_current.txt
@@ -230,7 +230,7 @@
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ClickableSurfaceShape {
   }
 
-  @SuppressCompatibility @androidx.compose.runtime.Stable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ColorScheme {
+  @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 border, long borderVariant, long scrim);
     method public androidx.tv.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 border, optional long borderVariant, optional long scrim);
     method public long getBackground();
@@ -294,16 +294,16 @@
   }
 
   public final class ColorSchemeKt {
-    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static long contentColorFor(androidx.tv.material3.ColorScheme, long backgroundColor);
+    method public static long contentColorFor(androidx.tv.material3.ColorScheme, long backgroundColor);
     method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public static long contentColorFor(long backgroundColor);
-    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.tv.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 border, optional long borderVariant, optional long scrim);
-    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.tv.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 border, optional long borderVariant, optional long scrim);
-    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static long surfaceColorAtElevation(androidx.tv.material3.ColorScheme, float elevation);
+    method public static androidx.tv.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 border, optional long borderVariant, optional long scrim);
+    method public static androidx.tv.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 border, optional long borderVariant, optional long scrim);
+    method public static long surfaceColorAtElevation(androidx.tv.material3.ColorScheme, float elevation);
   }
 
   public final class ContentColorKt {
-    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
-    property @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
+    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;
   }
 
   @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class DrawerState {
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/ColorScheme.kt b/tv/tv-material/src/main/java/androidx/tv/material3/ColorScheme.kt
index f6903d5..eda749a 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/ColorScheme.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/ColorScheme.kt
@@ -37,7 +37,6 @@
 /**
  * Returns a light Material color scheme.
  */
-@ExperimentalTvMaterial3Api
 fun lightColorScheme(
     primary: Color = ColorLightTokens.Primary,
     onPrimary: Color = ColorLightTokens.OnPrimary,
@@ -163,7 +162,6 @@
  * contrast is not required.
  * @property scrim Color of a scrim that obscures content.
  */
-@ExperimentalTvMaterial3Api
 @Stable
 class ColorScheme(
     primary: Color,
@@ -357,7 +355,6 @@
 /**
  * Returns a dark Material color scheme.
  */
-@ExperimentalTvMaterial3Api
 fun darkColorScheme(
     primary: Color = ColorDarkTokens.Primary,
     onPrimary: Color = ColorDarkTokens.OnPrimary,
@@ -438,7 +435,6 @@
  *
  * @see contentColorFor
  */
-@ExperimentalTvMaterial3Api
 fun ColorScheme.contentColorFor(backgroundColor: Color): Color =
     when (backgroundColor) {
         primary -> onPrimary
@@ -486,7 +482,6 @@
  * overlay corresponding to [elevation] applied. The overlay will only be applied to
  * [ColorScheme.surface].
  */
-@OptIn(ExperimentalTvMaterial3Api::class)
 internal fun ColorScheme.applyTonalElevation(backgroundColor: Color, elevation: Dp): Color {
     return if (backgroundColor == surface) {
         surfaceColorAtElevation(elevation)
@@ -504,7 +499,6 @@
  * overlaid on top of it.
 
  */
-@ExperimentalTvMaterial3Api
 fun ColorScheme.surfaceColorAtElevation(
     elevation: Dp
 ): Color {
@@ -527,7 +521,6 @@
  * changes will mutate the internal state of [this], and only cause composables that are reading the
  * specific changed value to recompose.
  */
-@OptIn(ExperimentalTvMaterial3Api::class)
 internal fun ColorScheme.updateColorSchemeFrom(other: ColorScheme) {
     primary = other.primary
     onPrimary = other.onPrimary
@@ -608,7 +601,6 @@
  * [ColorScheme.updateColorSchemeFrom]. To retrieve the current value of this CompositionLocal, use
  * [MaterialTheme.colorScheme].
  */
-@OptIn(ExperimentalTvMaterial3Api::class)
 internal val LocalColorScheme = staticCompositionLocalOf { lightColorScheme() }
 
 /**
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/ContentColor.kt b/tv/tv-material/src/main/java/androidx/tv/material3/ContentColor.kt
index 84d56da..d72d965 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/ContentColor.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/ContentColor.kt
@@ -32,6 +32,4 @@
  * Defaults to [Color.Black] if no color has been explicitly set.
  */
 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-@ExperimentalTvMaterial3Api
-@get:ExperimentalTvMaterial3Api
 val LocalContentColor = compositionLocalOf { Color.Black }
diff --git a/viewpager2/viewpager2/build.gradle b/viewpager2/viewpager2/build.gradle
index aeb25ad..bd1608a 100644
--- a/viewpager2/viewpager2/build.gradle
+++ b/viewpager2/viewpager2/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api(project(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
     implementation("androidx.core:core:1.3.2")
     api("androidx.fragment:fragment:1.1.0")
     api("androidx.recyclerview:recyclerview:1.3.1")
diff --git a/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ButtonTest.kt b/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ButtonTest.kt
index 58caf1d..d900da3 100644
--- a/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ButtonTest.kt
+++ b/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ButtonTest.kt
@@ -31,8 +31,6 @@
 import androidx.compose.foundation.shape.CutCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.State
-import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
@@ -343,17 +341,10 @@
             ) {
                 ButtonWithDefaults(
                     backgroundColor = { enabled ->
-                        rememberUpdatedState(
-                            if (enabled) enabledBackgroundColor else disabledBackgroundColor
-                        )
+                        if (enabled) enabledBackgroundColor else disabledBackgroundColor
                     },
                     border = { enabled ->
-                        return@ButtonWithDefaults rememberUpdatedState(
-                            BorderStroke(
-                                2.dp,
-                                if (enabled) enabledBorderColor else disabledBorderColor
-                            )
-                        )
+                        BorderStroke(2.dp, if (enabled) enabledBorderColor else disabledBorderColor)
                     },
                     enabled = status.enabled(),
                     modifier = Modifier.testTag(TEST_TAG)
@@ -401,12 +392,10 @@
         modifier: Modifier = Modifier,
         onClick: () -> Unit = {},
         enabled: Boolean = true,
-        backgroundColor: @Composable (enabled: Boolean) -> State<Color> = {
-            rememberUpdatedState(DEFAULT_SHAPE_COLOR)
-        },
+        backgroundColor: @Composable (enabled: Boolean) -> Color = { DEFAULT_SHAPE_COLOR },
         interactionSource: MutableInteractionSource? = null,
         shape: Shape = CircleShape,
-        border: @Composable (enabled: Boolean) -> State<BorderStroke?>? = { null },
+        border: @Composable (enabled: Boolean) -> BorderStroke? = { null },
         content: @Composable BoxScope.() -> Unit
     ) = Button(
         onClick = onClick,
diff --git a/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ToggleButtonTest.kt b/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ToggleButtonTest.kt
index af4e346..75d5229 100644
--- a/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ToggleButtonTest.kt
+++ b/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ToggleButtonTest.kt
@@ -948,10 +948,10 @@
                     .background(testBackground)
             ) {
                 val actualBorderColor = borderColor(enabled, checked).value
-                val border = remember { mutableStateOf(BorderStroke(2.dp, actualBorderColor)) }
+                val border = BorderStroke(2.dp, actualBorderColor)
                 RoundToggleButtonWithDefaults(
                     backgroundColor = backgroundColor,
-                    border = { _, _ -> return@RoundToggleButtonWithDefaults border },
+                    border = { _, _ -> border },
                     enabled = enabled,
                     checked = checked,
                     modifier = Modifier.testTag(TEST_TAG)
@@ -1003,7 +1003,7 @@
     enabled: Boolean = true,
     backgroundColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color> =
         { _, _ -> rememberUpdatedState(DEFAULT_SHAPE_COLOR) },
-    border: @Composable (enabled: Boolean, checked: Boolean) -> State<BorderStroke?>? =
+    border: @Composable (enabled: Boolean, checked: Boolean) -> BorderStroke? =
         { _, _ -> null },
     toggleButtonSize: Dp = 52.dp,
     interactionSource: MutableInteractionSource? = null,
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
index de88c13..c651af3 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
@@ -27,7 +27,6 @@
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
@@ -68,15 +67,15 @@
     onClick: () -> Unit,
     modifier: Modifier,
     enabled: Boolean,
-    backgroundColor: @Composable (enabled: Boolean) -> State<Color>,
+    backgroundColor: @Composable (enabled: Boolean) -> Color,
     interactionSource: MutableInteractionSource?,
     shape: Shape,
-    border: @Composable (enabled: Boolean) -> State<BorderStroke?>?,
+    border: @Composable (enabled: Boolean) -> BorderStroke?,
     buttonSize: Dp,
     ripple: Indication,
     content: @Composable BoxScope.() -> Unit,
 ) {
-    val borderStroke = border(enabled)?.value
+    val borderStroke = border(enabled)
     Box(
         contentAlignment = Alignment.Center,
         modifier = modifier
@@ -94,7 +93,7 @@
                 else Modifier
             )
             .background(
-                color = backgroundColor(enabled).value,
+                color = backgroundColor(enabled),
                 shape = shape
             ),
         content = content
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/ToggleButton.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/ToggleButton.kt
index 3dca20c..83d139d 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/ToggleButton.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/ToggleButton.kt
@@ -94,7 +94,7 @@
     modifier: Modifier,
     enabled: Boolean,
     backgroundColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
-    border: @Composable (enabled: Boolean, checked: Boolean) -> State<BorderStroke?>?,
+    border: @Composable (enabled: Boolean, checked: Boolean) -> BorderStroke?,
     toggleButtonSize: Dp,
     interactionSource: MutableInteractionSource?,
     shape: Shape,
@@ -102,7 +102,7 @@
     content: @Composable BoxScope.() -> Unit,
 ) {
     // Round toggle button
-    val borderStroke = border(enabled, checked)?.value
+    val borderStroke = border(enabled, checked)
     Box(
         contentAlignment = Alignment.Center,
         modifier = modifier
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Button.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Button.kt
index b9a8cb2..da69959 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Button.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Button.kt
@@ -158,10 +158,10 @@
         onClick = onClick,
         modifier = modifier,
         enabled = enabled,
-        backgroundColor = { colors.backgroundColor(it) },
+        backgroundColor = { colors.backgroundColor(it).value },
         interactionSource = interactionSource,
         shape = shape,
-        border = { border.borderStroke(enabled = it) },
+        border = { border.borderStroke(enabled = it).value },
         buttonSize = ButtonDefaults.DefaultButtonSize,
         ripple = rippleOrFallbackImplementation(),
         content = provideScopeContent(
@@ -341,10 +341,10 @@
             .padding(backgroundPadding)
             .requiredSize(ButtonDefaults.ExtraSmallButtonSize),
         enabled = enabled,
-        backgroundColor = { colors.backgroundColor(it) },
+        backgroundColor = { colors.backgroundColor(it).value },
         interactionSource = interactionSource,
         shape = shape,
-        border = { border.borderStroke(it) },
+        border = { border.borderStroke(it).value },
         buttonSize = ButtonDefaults.ExtraSmallButtonSize,
         ripple = rippleOrFallbackImplementation(),
         content = provideScopeContent(
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index 496d3e6..d722a6e 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -517,7 +517,6 @@
     method public long getUncheckedContentColor();
     method public long getUncheckedSecondaryContentColor();
     method public long getUncheckedSplitContainerColor();
-    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> splitContainerColor(boolean enabled, boolean checked);
     property public final long checkedContainerColor;
     property public final long checkedContentColor;
     property public final long checkedSecondaryContentColor;
@@ -640,8 +639,6 @@
   @androidx.compose.runtime.Immutable public final class ToggleButtonColors {
     ctor public ToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
     ctor public ToggleButtonColors(long checkedContainerColor, long checkedContentColor, long checkedSecondaryContentColor, long checkedIconColor, long uncheckedContainerColor, long uncheckedContentColor, long uncheckedSecondaryContentColor, long uncheckedIconColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledCheckedSecondaryContentColor, long disabledCheckedIconColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor, long disabledUncheckedSecondaryContentColor, long disabledUncheckedIconColor);
-    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> containerColor(boolean enabled, boolean checked);
-    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled, boolean checked);
     method public long getCheckedContainerColor();
     method public long getCheckedContentColor();
     method public long getCheckedIconColor();
@@ -658,8 +655,6 @@
     method public long getUncheckedContentColor();
     method public long getUncheckedIconColor();
     method public long getUncheckedSecondaryContentColor();
-    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> iconColor(boolean enabled, boolean checked);
-    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> secondaryContentColor(boolean enabled, boolean checked);
     property public final long checkedContainerColor;
     property public final long checkedContentColor;
     property public final long checkedIconColor;
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index 496d3e6..d722a6e 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -517,7 +517,6 @@
     method public long getUncheckedContentColor();
     method public long getUncheckedSecondaryContentColor();
     method public long getUncheckedSplitContainerColor();
-    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> splitContainerColor(boolean enabled, boolean checked);
     property public final long checkedContainerColor;
     property public final long checkedContentColor;
     property public final long checkedSecondaryContentColor;
@@ -640,8 +639,6 @@
   @androidx.compose.runtime.Immutable public final class ToggleButtonColors {
     ctor public ToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
     ctor public ToggleButtonColors(long checkedContainerColor, long checkedContentColor, long checkedSecondaryContentColor, long checkedIconColor, long uncheckedContainerColor, long uncheckedContentColor, long uncheckedSecondaryContentColor, long uncheckedIconColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledCheckedSecondaryContentColor, long disabledCheckedIconColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor, long disabledUncheckedSecondaryContentColor, long disabledUncheckedIconColor);
-    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> containerColor(boolean enabled, boolean checked);
-    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled, boolean checked);
     method public long getCheckedContainerColor();
     method public long getCheckedContentColor();
     method public long getCheckedIconColor();
@@ -658,8 +655,6 @@
     method public long getUncheckedContentColor();
     method public long getUncheckedIconColor();
     method public long getUncheckedSecondaryContentColor();
-    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> iconColor(boolean enabled, boolean checked);
-    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> secondaryContentColor(boolean enabled, boolean checked);
     property public final long checkedContainerColor;
     property public final long checkedContentColor;
     property public final long checkedIconColor;
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
index 6dce08a..5042316 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
@@ -916,11 +916,11 @@
     setContentWithTheme {
         val buttonColors = expectedColor()
         containerColor = (
-            (buttonColors.containerPainter(status.enabled()).value as ColorPainter).color)
+            (buttonColors.containerPainter(status.enabled()) as ColorPainter).color)
             .compositeOver(testBackgroundColor)
-        labelColor = buttonColors.contentColor(status.enabled()).value
-        secondaryLabelColor = buttonColors.secondaryContentColor(status.enabled()).value
-        iconColor = buttonColors.iconColor(status.enabled()).value
+        labelColor = buttonColors.contentColor(status.enabled())
+        secondaryLabelColor = buttonColors.secondaryContentColor(status.enabled())
+        iconColor = buttonColors.iconColor(status.enabled())
 
         Box(
             Modifier
@@ -1051,7 +1051,7 @@
     setContentWithTheme {
         background = MaterialTheme.colorScheme.surface
         Box(Modifier.background(background)) {
-            buttonColor = (colors().containerPainter(true).value as ColorPainter).color
+            buttonColor = (colors().containerPainter(true) as ColorPainter).color
             if (buttonColor == Color.Transparent) {
                 buttonColor = background
             }
@@ -1089,10 +1089,10 @@
     var actualIconColor = Color.Transparent
 
     setContentWithTheme {
-        containerColor = ((colors().containerPainter(status.enabled()).value as ColorPainter).color)
+        containerColor = ((colors().containerPainter(status.enabled()) as ColorPainter).color)
             .compositeOver(testBackgroundColor)
-        labelColor = colors().contentColor(status.enabled()).value
-        iconColor = colors().iconColor(status.enabled()).value
+        labelColor = colors().contentColor(status.enabled())
+        iconColor = colors().iconColor(status.enabled())
 
         Box(
             Modifier
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
index 13f139f..d9d875a 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
@@ -538,7 +538,7 @@
     setContentWithTheme {
         background = MaterialTheme.colorScheme.surface
         Box(Modifier.background(background)) {
-            buttonColor = colors().containerColor(true).value
+            buttonColor = colors().containerColor(true)
             if (buttonColor == Color.Transparent) {
                 buttonColor = background
             }
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
index 6cddf52..87e1a71 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
@@ -532,7 +532,7 @@
     setContentWithTheme {
         background = MaterialTheme.colorScheme.surface
         Box(Modifier.background(background)) {
-            buttonColor = colors().containerColor(true).value
+            buttonColor = colors().containerColor(true)
             if (buttonColor == Color.Transparent) {
                 buttonColor = background
             }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
index 3640475..6412358 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
@@ -39,9 +39,8 @@
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.State
+import androidx.compose.runtime.Stable
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
@@ -379,8 +378,7 @@
  * label and secondaryLabel contents should be consistently aligned.
  * @param icon A slot for providing the button's icon. The contents are expected to be a
  * horizontally and vertically aligned icon of size [ButtonDefaults.IconSize] or
- * [ButtonDefaults.LargeIconSize]. In order to correctly render when the Button is not enabled,
- * the icon must set its alpha value to [LocalContentAlpha].
+ * [ButtonDefaults.LargeIconSize].
  * @param enabled Controls the enabled state of the button. When `false`, this button will not
  * be clickable
  * @param shape Defines the button's shape. It is strongly recommended to use the default as this
@@ -473,8 +471,7 @@
  * label and secondaryLabel contents should be consistently aligned.
  * @param icon A slot for providing the button's icon. The contents are expected to be a
  * horizontally and vertically aligned icon of size [ButtonDefaults.IconSize] or
- * [ButtonDefaults.LargeIconSize]. In order to correctly render when the Button is not enabled,
- * the icon must set its alpha value to [LocalContentAlpha].
+ * [ButtonDefaults.LargeIconSize].
  * @param enabled Controls the enabled state of the button. When `false`, this button will not
  * be clickable
  * @param shape Defines the button's shape. It is strongly recommended to use the default as this
@@ -561,8 +558,7 @@
  * label and secondaryLabel contents should be consistently aligned.
  * @param icon A slot for providing the button's icon. The contents are expected to be a
  * horizontally and vertically aligned icon of size [ButtonDefaults.IconSize] or
- * [ButtonDefaults.LargeIconSize]. In order to correctly render when the Button is not enabled,
- * the icon must set its alpha value to [LocalContentAlpha].
+ * [ButtonDefaults.LargeIconSize].
  * @param enabled Controls the enabled state of the button. When `false`, this button will not
  * be clickable
  * @param shape Defines the button's shape. It is strongly recommended to use the default as this
@@ -648,8 +644,7 @@
  * label and secondaryLabel contents should be consistently aligned.
  * @param icon A slot for providing the button's icon. The contents are expected to be a
  * horizontally and vertically aligned icon of size [ButtonDefaults.IconSize] or
- * [ButtonDefaults.LargeIconSize]. In order to correctly render when the Button is not enabled,
- * the icon must set its alpha value to [LocalContentAlpha].
+ * [ButtonDefaults.LargeIconSize].
  * @param enabled Controls the enabled state of the button. When `false`, this button will not
  * be clickable
  * @param shape Defines the button's shape. It is strongly recommended to use the default as this
@@ -754,9 +749,7 @@
  * which is "start" aligned if there is an icon preset and "center" aligned if not.
  * @param icon A slot for providing the button's icon. The contents are expected to be a
  * horizontally and vertically aligned icon of size [ButtonDefaults.SmallIconSize] when used
- * with a label or [ButtonDefaults.IconSize] when used as the only content in the button. In order
- * to correctly render when the button is not enabled the icon must set its
- * alpha value to [LocalContentAlpha].
+ * with a label or [ButtonDefaults.IconSize] when used as the only content in the button.
  * @param colors [ButtonColors] that will be used to resolve the background and content color for
  * this button in different states. See [ButtonDefaults.filledButtonColors].
  * @param enabled Controls the enabled state of the button. When `false`, this button will not
@@ -838,9 +831,10 @@
     /**
      * Creates a [ButtonColors] with colored background and contrasting content color,
      * the defaults for high emphasis buttons like [Button], for the primary, most important
-     * or most common action on a screen. If a button is disabled then the content will have
-     * an alpha([ContentAlpha.disabled]) value applied and container/border colors will be
-     * muted.
+     * or most common action on a screen.
+     *
+     * If a button is disabled then the content will have an alpha([DisabledContentAlpha]) value
+     * applied and container will have an alpha([DisabledContainerAlpha]) value applied.
      *
      * @param containerColor The background color of this [Button] when enabled
      * @param contentColor The content color of this [Button] when enabled
@@ -893,8 +887,8 @@
      * other onscreen elements, such as final or unblocking actions in a flow with less emphasis
      * than [filledButtonColors].
      *
-     * If a button is disabled then the content will have an alpha([ContentAlpha.disabled])
-     * value applied and container/border colors will be muted.
+     * If a button is disabled then the content will have an alpha([DisabledContentAlpha])
+     * value applied and container will have alpha ([DisabledContainerAlpha]) value applied.
      *
      * @param containerColor The background color of this [Button] when enabled
      * @param contentColor The content color of this [Button] when enabled
@@ -947,8 +941,8 @@
      * [ButtonDefaults.outlinedButtonBorder]), the defaults for medium emphasis buttons
      * like [OutlinedButton], for important, non-primary actions that need attention.
      *
-     * If a button is disabled then the content will have an alpha([ContentAlpha.disabled])
-     * value applied and container/border colors will be muted.
+     * If a button is disabled then the content will have an alpha([DisabledContentAlpha])
+     * value applied and container will have an alpha([DisabledContainerAlpha]) applied.
      *
      * @param contentColor The content color of this [Button] when enabled
      * @param secondaryContentColor The secondary content color of this [Button] when enabled, used
@@ -993,8 +987,8 @@
      * buttons like [ChildButton]. Use [childButtonColors] for optional or supplementary
      * actions with the least amount of prominence.
      *
-     * If a button is disabled then the content will have an alpha([ContentAlpha.disabled])
-     * value applied and container/border colors will be muted.
+     * If a button is disabled then the content will have an alpha([DisabledContentAlpha])
+     * value applied and container will have an alpha([DisabledContainerAlpha]) value applied.
      *
      * @param contentColor The content color of this [Button] when enabled
      * @param secondaryContentColor The secondary content color of this [Button] when enabled, used
@@ -1314,11 +1308,9 @@
      *
      * @param enabled whether the button is enabled
      */
-    @Composable
-    internal fun containerPainter(enabled: Boolean): State<Painter> {
-        return rememberUpdatedState(
-            if (enabled) containerPainter else disabledContainerPainter
-        )
+    @Stable
+    internal fun containerPainter(enabled: Boolean): Painter {
+        return if (enabled) containerPainter else disabledContainerPainter
     }
 
     /**
@@ -1326,11 +1318,9 @@
      *
      * @param enabled whether the button is enabled
      */
-    @Composable
-    internal fun contentColor(enabled: Boolean): State<Color> {
-        return rememberUpdatedState(
-            if (enabled) contentColor else disabledContentColor
-        )
+    @Stable
+    internal fun contentColor(enabled: Boolean): Color {
+        return if (enabled) contentColor else disabledContentColor
     }
 
     /**
@@ -1338,11 +1328,9 @@
      *
      * @param enabled Whether the button is enabled
      */
-    @Composable
-    internal fun secondaryContentColor(enabled: Boolean): State<Color> {
-        return rememberUpdatedState(
-            if (enabled) secondaryContentColor else disabledSecondaryContentColor
-        )
+    @Stable
+    internal fun secondaryContentColor(enabled: Boolean): Color {
+        return if (enabled) secondaryContentColor else disabledSecondaryContentColor
     }
 
     /**
@@ -1350,9 +1338,9 @@
      *
      * @param enabled Whether the button is enabled
      */
-    @Composable
-    internal fun iconColor(enabled: Boolean): State<Color> {
-        return rememberUpdatedState(if (enabled) iconColor else disabledIconColor)
+    @Stable
+    internal fun iconColor(enabled: Boolean): Color {
+        return if (enabled) iconColor else disabledIconColor
     }
 
     override fun equals(other: Any?): Boolean {
@@ -1420,7 +1408,7 @@
             .clip(shape = shape)
             .width(intrinsicSize = IntrinsicSize.Max)
             .paint(
-                painter = colors.containerPainter(enabled = enabled).value,
+                painter = colors.containerPainter(enabled = enabled),
                 contentScale = ContentScale.Crop
             )
             .clickable(
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
index 5a9f0e2..aeb9ea6d 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
@@ -22,8 +22,7 @@
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.Stable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
@@ -90,7 +89,7 @@
         backgroundColor = { colors.containerColor(enabled = it) },
         interactionSource = interactionSource,
         shape = shape,
-        border = { rememberUpdatedState(border) },
+        border = { border },
         buttonSize = IconButtonDefaults.DefaultButtonSize,
         ripple = rippleOrFallbackImplementation(),
         content = provideScopeContent(
@@ -312,7 +311,7 @@
         backgroundColor = { isEnabled, isChecked ->
             colors.containerColor(enabled = isEnabled, checked = isChecked)
         },
-        border = { _, _ -> rememberUpdatedState(newValue = border) },
+        border = { _, _ -> border },
         toggleButtonSize = IconButtonDefaults.DefaultButtonSize,
         interactionSource = interactionSource,
         shape = shape,
@@ -455,7 +454,7 @@
      * Creates a [ToggleButtonColors] for a [IconToggleButton]
      * - by default, a colored background with a contrasting content color.
      * If the button is disabled, then the colors will have an alpha
-     * ([ContentAlpha.disabled]) value applied.
+     * ([DisabledContentAlpha] and [DisabledContainerAlpha]) value applied.
      *
      * @param checkedContainerColor The container color of this [IconToggleButton] when enabled
      * and checked
@@ -572,9 +571,9 @@
      *
      * @param enabled whether the icon button is enabled
      */
-    @Composable
-    internal fun containerColor(enabled: Boolean): State<Color> {
-        return rememberUpdatedState(if (enabled) containerColor else disabledContainerColor)
+    @Stable
+    internal fun containerColor(enabled: Boolean): Color {
+        return if (enabled) containerColor else disabledContainerColor
     }
 
     /**
@@ -582,9 +581,9 @@
      *
      * @param enabled whether the icon button is enabled
      */
-    @Composable
-    internal fun contentColor(enabled: Boolean): State<Color> {
-        return rememberUpdatedState(if (enabled) contentColor else disabledContentColor)
+    @Stable
+    internal fun contentColor(enabled: Boolean): Color {
+        return if (enabled) contentColor else disabledContentColor
     }
 
     override fun equals(other: Any?): Boolean {
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/InteractiveComponentSize.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/InteractiveComponentSize.kt
index dc96844..cb3ace1 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/InteractiveComponentSize.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/InteractiveComponentSize.kt
@@ -19,12 +19,14 @@
 import androidx.compose.runtime.ProvidableCompositionLocal
 import androidx.compose.runtime.staticCompositionLocalOf
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
-import androidx.compose.ui.layout.LayoutModifier
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.currentValueOf
+import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
@@ -42,20 +44,56 @@
  * This modifier is not needed for touch target expansion to happen. It only affects layout, to make
  * sure there is adequate space for touch target expansion.
  */
-@OptIn(ExperimentalWearMaterial3Api::class)
-fun Modifier.minimumInteractiveComponentSize(): Modifier = composed(
-    inspectorInfo = debugInspectorInfo {
+fun Modifier.minimumInteractiveComponentSize(): Modifier = this then MinimumInteractiveModifier
+
+internal object MinimumInteractiveModifier : ModifierNodeElement<MinimumInteractiveModifierNode>() {
+    override fun create(): MinimumInteractiveModifierNode = MinimumInteractiveModifierNode()
+
+    override fun update(node: MinimumInteractiveModifierNode) {}
+
+    override fun InspectorInfo.inspectableProperties() {
         name = "minimumInteractiveComponentSize"
         // TODO: b/214589635 - surface this information through the layout inspector in a better way
         //  - for now just add some information to help developers debug what this size represents.
         properties["README"] = "Reserves at least 48.dp in size to disambiguate touch " +
             "interactions if the element would measure smaller"
     }
-) {
-    if (LocalMinimumInteractiveComponentEnforcement.current) {
-        MinimumInteractiveComponentSizeModifier(minimumInteractiveComponentSize)
-    } else {
-        Modifier
+
+    override fun hashCode(): Int = System.identityHashCode(this)
+    override fun equals(other: Any?): Boolean = (other === this)
+}
+
+internal class MinimumInteractiveModifierNode :
+    Modifier.Node(),
+    CompositionLocalConsumerModifierNode,
+    LayoutModifierNode {
+
+    @OptIn(ExperimentalWearMaterial3Api::class)
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        val enforcement = isAttached && currentValueOf(LocalMinimumInteractiveComponentEnforcement)
+        val size = minimumInteractiveComponentSize
+        val placeable = measurable.measure(constraints)
+
+        // Be at least as big as the minimum dimension in both dimensions
+        val width = if (enforcement) {
+            maxOf(placeable.width, size.width.roundToPx())
+        } else {
+            placeable.width
+        }
+        val height = if (enforcement) {
+            maxOf(placeable.height, size.height.roundToPx())
+        } else {
+            placeable.height
+        }
+
+        return layout(width, height) {
+            val centerX = ((width - placeable.width) / 2f).roundToInt()
+            val centerY = ((height - placeable.height) / 2f).roundToInt()
+            placeable.place(centerX, centerY)
+        }
     }
 }
 
@@ -73,33 +111,4 @@
 val LocalMinimumInteractiveComponentEnforcement: ProvidableCompositionLocal<Boolean> =
     staticCompositionLocalOf { true }
 
-private class MinimumInteractiveComponentSizeModifier(val size: DpSize) : LayoutModifier {
-    override fun MeasureScope.measure(
-        measurable: Measurable,
-        constraints: Constraints
-    ): MeasureResult {
-
-        val placeable = measurable.measure(constraints)
-
-        // Be at least as big as the minimum dimension in both dimensions
-        val width = maxOf(placeable.width, size.width.roundToPx())
-        val height = maxOf(placeable.height, size.height.roundToPx())
-
-        return layout(width, height) {
-            val centerX = ((width - placeable.width) / 2f).roundToInt()
-            val centerY = ((height - placeable.height) / 2f).roundToInt()
-            placeable.place(centerX, centerY)
-        }
-    }
-
-    override fun equals(other: Any?): Boolean {
-        val otherModifier = other as? MinimumInteractiveComponentSizeModifier ?: return false
-        return size == otherModifier.size
-    }
-
-    override fun hashCode(): Int {
-        return size.hashCode()
-    }
-}
-
 private val minimumInteractiveComponentSize: DpSize = DpSize(48.dp, 48.dp)
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Providers.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Providers.kt
index 18fc72a..82d990d 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Providers.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Providers.kt
@@ -23,6 +23,19 @@
 import androidx.compose.ui.text.TextStyle
 
 internal fun <T> provideScopeContent(
+    contentColor: Color,
+    textStyle: TextStyle,
+    content: (@Composable T.() -> Unit)
+): (@Composable T.() -> Unit) = {
+    CompositionLocalProvider(
+        LocalContentColor provides contentColor,
+        LocalTextStyle provides textStyle,
+    ) {
+        content()
+    }
+}
+
+internal fun <T> provideScopeContent(
     contentColor: State<Color>,
     textStyle: TextStyle,
     content: (@Composable T.() -> Unit)
@@ -37,6 +50,17 @@
 }
 
 internal fun <T> provideScopeContent(
+    color: Color,
+    content: (@Composable T.() -> Unit)
+): (@Composable T.() -> Unit) = {
+    CompositionLocalProvider(
+        LocalContentColor provides color,
+    ) {
+        content()
+    }
+}
+
+internal fun <T> provideScopeContent(
     color: State<Color>,
     content: (@Composable T.() -> Unit)
 ): (@Composable T.() -> Unit) = {
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
index 15373eb..ac94013 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
@@ -22,8 +22,7 @@
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.Stable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
@@ -96,7 +95,7 @@
         backgroundColor = { colors.containerColor(enabled = it) },
         interactionSource = interactionSource,
         shape = shape,
-        border = { rememberUpdatedState(border) },
+        border = { border },
         buttonSize = TextButtonDefaults.DefaultButtonSize,
         ripple = rippleOrFallbackImplementation(),
         content = provideScopeContent(
@@ -164,7 +163,7 @@
         backgroundColor = { isEnabled, isChecked ->
             colors.containerColor(enabled = isEnabled, checked = isChecked)
         },
-        border = { _, _ -> rememberUpdatedState(border) },
+        border = { _, _ -> border },
         toggleButtonSize = TextButtonDefaults.DefaultButtonSize,
         interactionSource = interactionSource,
         shape = shape,
@@ -312,7 +311,7 @@
      * Creates a [ToggleButtonColors] for a [TextToggleButton]
      * - by default, a colored background with a contrasting content color.
      * If the button is disabled, then the
-     * colors will have an alpha ([ContentAlpha.disabled]) value applied.
+     * colors will have an alpha ([DisabledContainerAlpha] or [DisabledContentAlpha]) value applied.
      *
      * @param checkedContainerColor the container color of this [TextToggleButton] when enabled
      * and checked
@@ -411,9 +410,9 @@
      *
      * @param enabled whether the text button is enabled
      */
-    @Composable
-    internal fun containerColor(enabled: Boolean): State<Color> {
-        return rememberUpdatedState(if (enabled) containerColor else disabledContainerColor)
+    @Stable
+    internal fun containerColor(enabled: Boolean): Color {
+        return if (enabled) containerColor else disabledContainerColor
     }
 
     /**
@@ -421,9 +420,9 @@
      *
      * @param enabled whether the text button is enabled
      */
-    @Composable
-    internal fun contentColor(enabled: Boolean): State<Color> {
-        return rememberUpdatedState(if (enabled) contentColor else disabledContentColor)
+    @Stable
+    internal fun contentColor(enabled: Boolean): Color {
+        return if (enabled) contentColor else disabledContentColor
     }
 
     override fun equals(other: Any?): Boolean {
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButton.kt
index 04506ec..a063df0 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButton.kt
@@ -571,7 +571,7 @@
      * @param checked Whether the toggle button is checked
      */
     @Composable
-    fun containerColor(enabled: Boolean, checked: Boolean): State<Color> =
+    internal fun containerColor(enabled: Boolean, checked: Boolean): State<Color> =
         animateSelectionColor(
             enabled = enabled,
             checked = checked,
@@ -590,7 +590,7 @@
      * @param checked Whether the toggle button is checked
      */
     @Composable
-    fun contentColor(enabled: Boolean, checked: Boolean): State<Color> =
+    internal fun contentColor(enabled: Boolean, checked: Boolean): State<Color> =
         animateSelectionColor(
             enabled = enabled,
             checked = checked,
@@ -608,7 +608,7 @@
      * @param checked Whether the ToggleButton is currently checked or unchecked.
      */
     @Composable
-    fun secondaryContentColor(enabled: Boolean, checked: Boolean): State<Color> =
+    internal fun secondaryContentColor(enabled: Boolean, checked: Boolean): State<Color> =
         animateSelectionColor(
             enabled = enabled,
             checked = checked,
@@ -627,7 +627,7 @@
      * @param checked Whether the ToggleButton is currently checked or unchecked.
      */
     @Composable
-    fun iconColor(enabled: Boolean, checked: Boolean): State<Color> =
+    internal fun iconColor(enabled: Boolean, checked: Boolean): State<Color> =
         animateSelectionColor(
             enabled = enabled,
             checked = checked,
@@ -811,7 +811,7 @@
      * @param checked Whether the [SplitToggleButton] is currently checked or unchecked.
      */
     @Composable
-    fun splitContainerColor(enabled: Boolean, checked: Boolean): State<Color> =
+    internal fun splitContainerColor(enabled: Boolean, checked: Boolean): State<Color> =
         animateSelectionColor(
             enabled = enabled,
             checked = checked,
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
index 3a68720..1d1c35c 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
@@ -245,6 +245,6 @@
 
         /** The current version of the Tiles schema in use. */
         public static final VersionInfo CURRENT =
-                VersionInfo.newBuilder().setMajor(1).setMinor(200).build();
+                VersionInfo.newBuilder().setMajor(1).setMinor(400).build();
     }
 }
diff --git a/wear/watchface/watchface-complications-data/api/current.ignore b/wear/watchface/watchface-complications-data/api/current.ignore
index 046473b..fb686a5 100644
--- a/wear/watchface/watchface-complications-data/api/current.ignore
+++ b/wear/watchface/watchface-complications-data/api/current.ignore
@@ -3,5 +3,79 @@
     Added method androidx.wear.watchface.complications.data.ColorRamp.isInterpolated()
 
 
+ChangedType: androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.LongTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.LongTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.LongTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder
+
+
 RemovedMethod: androidx.wear.watchface.complications.data.ColorRamp#getInterpolated():
     Removed method androidx.wear.watchface.complications.data.ColorRamp.getInterpolated()
+RemovedMethod: androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
diff --git a/wear/watchface/watchface-complications-data/api/current.txt b/wear/watchface/watchface-complications-data/api/current.txt
index 8f499f5..1740d3f 100644
--- a/wear/watchface/watchface-complications-data/api/current.txt
+++ b/wear/watchface/watchface-complications-data/api/current.txt
@@ -143,11 +143,11 @@
     ctor public GoalProgressComplicationData.Builder(float value, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData build();
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.GoalProgressComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -176,11 +176,11 @@
   public static final class LongTextComplicationData.Builder {
     ctor public LongTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.LongTextComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? icon);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -220,10 +220,10 @@
   public static final class MonochromaticImageComplicationData.Builder {
     ctor public MonochromaticImageComplicationData.Builder(androidx.wear.watchface.complications.data.MonochromaticImage monochromaticImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method public final androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.MonochromaticImageComplicationData? fallback);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -263,11 +263,11 @@
   public static final class NoPermissionComplicationData.Builder {
     ctor public NoPermissionComplicationData.Builder();
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.NoPermissionComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -298,10 +298,10 @@
   public static final class PhotoImageComplicationData.Builder {
     ctor public PhotoImageComplicationData.Builder(android.graphics.drawable.Icon photoImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method public final androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.PhotoImageComplicationData? fallback);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -358,11 +358,11 @@
     ctor public RangedValueComplicationData.Builder(float value, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData build();
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.RangedValueComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -393,11 +393,11 @@
   public static final class ShortTextComplicationData.Builder {
     ctor public ShortTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.ShortTextComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -439,10 +439,10 @@
   public static final class SmallImageComplicationData.Builder {
     ctor public SmallImageComplicationData.Builder(androidx.wear.watchface.complications.data.SmallImage smallImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method public final androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.SmallImageComplicationData? fallback);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -550,12 +550,12 @@
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class WeightedElementsComplicationData.Builder {
     ctor public WeightedElementsComplicationData.Builder(java.util.List<androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Element> elements, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.WeightedElementsComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setElementBackgroundColor(@ColorInt int elementBackgroundColor);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
diff --git a/wear/watchface/watchface-complications-data/api/restricted_current.ignore b/wear/watchface/watchface-complications-data/api/restricted_current.ignore
index 046473b..fb686a5 100644
--- a/wear/watchface/watchface-complications-data/api/restricted_current.ignore
+++ b/wear/watchface/watchface-complications-data/api/restricted_current.ignore
@@ -3,5 +3,79 @@
     Added method androidx.wear.watchface.complications.data.ColorRamp.isInterpolated()
 
 
+ChangedType: androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.LongTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.LongTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.LongTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder#setDataSource(android.content.ComponentName):
+    Method androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder.setDataSource has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder#setDisplayPolicy(int):
+    Method androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder.setDisplayPolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder
+ChangedType: androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder#setPersistencePolicy(int):
+    Method androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder.setPersistencePolicy has changed return type from java.lang.BuilderT to androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder
+
+
 RemovedMethod: androidx.wear.watchface.complications.data.ColorRamp#getInterpolated():
     Removed method androidx.wear.watchface.complications.data.ColorRamp.getInterpolated()
+RemovedMethod: androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
+RemovedMethod: androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder#setDynamicValueInvalidationFallback(BuiltT):
+    Removed method androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder.setDynamicValueInvalidationFallback(BuiltT)
diff --git a/wear/watchface/watchface-complications-data/api/restricted_current.txt b/wear/watchface/watchface-complications-data/api/restricted_current.txt
index 8f499f5..1740d3f 100644
--- a/wear/watchface/watchface-complications-data/api/restricted_current.txt
+++ b/wear/watchface/watchface-complications-data/api/restricted_current.txt
@@ -143,11 +143,11 @@
     ctor public GoalProgressComplicationData.Builder(float value, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData build();
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.GoalProgressComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -176,11 +176,11 @@
   public static final class LongTextComplicationData.Builder {
     ctor public LongTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.LongTextComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? icon);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -220,10 +220,10 @@
   public static final class MonochromaticImageComplicationData.Builder {
     ctor public MonochromaticImageComplicationData.Builder(androidx.wear.watchface.complications.data.MonochromaticImage monochromaticImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method public final androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.MonochromaticImageComplicationData? fallback);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -263,11 +263,11 @@
   public static final class NoPermissionComplicationData.Builder {
     ctor public NoPermissionComplicationData.Builder();
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.NoPermissionComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -298,10 +298,10 @@
   public static final class PhotoImageComplicationData.Builder {
     ctor public PhotoImageComplicationData.Builder(android.graphics.drawable.Icon photoImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method public final androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.PhotoImageComplicationData? fallback);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -358,11 +358,11 @@
     ctor public RangedValueComplicationData.Builder(float value, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData build();
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.RangedValueComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -393,11 +393,11 @@
   public static final class ShortTextComplicationData.Builder {
     ctor public ShortTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.ShortTextComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -439,10 +439,10 @@
   public static final class SmallImageComplicationData.Builder {
     ctor public SmallImageComplicationData.Builder(androidx.wear.watchface.complications.data.SmallImage smallImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method public final androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.SmallImageComplicationData? fallback);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -550,12 +550,12 @@
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class WeightedElementsComplicationData.Builder {
     ctor public WeightedElementsComplicationData.Builder(java.util.List<androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Element> elements, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData build();
-    method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
-    method public final BuilderT setDynamicValueInvalidationFallback(BuiltT? fallback);
+    method public final androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setDisplayPolicy(int displayPolicy);
+    method public final androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setDynamicValueInvalidationFallback(androidx.wear.watchface.complications.data.WeightedElementsComplicationData? fallback);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setElementBackgroundColor(@ColorInt int elementBackgroundColor);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
-    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
diff --git a/window/extensions/extensions/build.gradle b/window/extensions/extensions/build.gradle
index 991473b..3d54101 100644
--- a/window/extensions/extensions/build.gradle
+++ b/window/extensions/extensions/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     implementation("androidx.annotation:annotation:1.6.0")
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation-experimental:1.4.0")
     implementation("androidx.window.extensions.core:core:1.0.0")
 
     testImplementation(libs.robolectric)
diff --git a/window/integration-tests/configuration-change-tests/src/androidTest/java/androidx/window/integration/TestConsumer.kt b/window/integration-tests/configuration-change-tests/src/androidTest/java/androidx/window/integration/TestConsumer.kt
index 05a18ec..01f29fe 100644
--- a/window/integration-tests/configuration-change-tests/src/androidTest/java/androidx/window/integration/TestConsumer.kt
+++ b/window/integration-tests/configuration-change-tests/src/androidTest/java/androidx/window/integration/TestConsumer.kt
@@ -40,9 +40,9 @@
     /**
      * Appends the new value at the end of the mutable list values.
      */
-    override fun accept(numLayoutFeatures: T) {
+    override fun accept(value: T) {
         valueLock.withLock {
-            values.add(numLayoutFeatures)
+            values.add(value)
         }
         countDownLock.withLock {
             valueLatch.countDown()
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt
index 16687a5..5d1bbe7 100644
--- a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt
+++ b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt
@@ -83,7 +83,7 @@
          * @throws IllegalArgumentException if the height is negative
          */
         internal fun compute(dpHeight: Float): WindowHeightSizeClass {
-            require(dpHeight > 0) { "Height must be positive, received $dpHeight" }
+            require(dpHeight >= 0) { "Height must be positive, received $dpHeight" }
             return when {
                 dpHeight < 480 -> COMPACT
                 dpHeight < 900 -> MEDIUM
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt
index 0f71802..c2d6cc0 100644
--- a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt
+++ b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt
@@ -80,7 +80,7 @@
          * @throws IllegalArgumentException if the width is negative
          */
         internal fun compute(dpWidth: Float): WindowWidthSizeClass {
-            require(dpWidth > 0) { "Width must be positive, received $dpWidth" }
+            require(dpWidth >= 0) { "Width must be positive, received $dpWidth" }
             return when {
                 dpWidth < 600 -> COMPACT
                 dpWidth < 840 -> MEDIUM
diff --git a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt
index 846bbfc..939748d 100644
--- a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt
+++ b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt
@@ -19,6 +19,7 @@
 import androidx.window.core.ExperimentalWindowCoreApi
 import kotlin.test.Test
 import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
 
 /**
  * Tests for [WindowSizeClass] that verify construction.
@@ -77,4 +78,41 @@
         assertEquals(first, second)
         assertEquals(first.hashCode(), second.hashCode())
     }
+
+    @Test
+    @Suppress("DEPRECATION")
+    fun truncated_float_does_not_throw() {
+        val sizeClass = WindowSizeClass.compute(0.5f, 0.5f)
+
+        val widthSizeClass = sizeClass.windowWidthSizeClass
+        val heightSizeClass = sizeClass.windowHeightSizeClass
+
+        assertEquals(WindowWidthSizeClass.COMPACT, widthSizeClass)
+        assertEquals(WindowHeightSizeClass.COMPACT, heightSizeClass)
+    }
+
+    @Test
+    fun zero_size_class_does_not_throw() {
+        val sizeClass = WindowSizeClass(0, 0)
+
+        val widthSizeClass = sizeClass.windowWidthSizeClass
+        val heightSizeClass = sizeClass.windowHeightSizeClass
+
+        assertEquals(WindowWidthSizeClass.COMPACT, widthSizeClass)
+        assertEquals(WindowHeightSizeClass.COMPACT, heightSizeClass)
+    }
+
+    @Test
+    fun negative_width_throws() {
+        assertFailsWith(IllegalArgumentException::class) {
+            WindowSizeClass(-1, 0)
+        }
+    }
+
+    @Test
+    fun negative_height_throws() {
+        assertFailsWith(IllegalArgumentException::class) {
+            WindowSizeClass(0, -1)
+        }
+    }
 }
diff --git a/window/window-java/src/androidTest/java/androidx/window/java/TestConsumer.kt b/window/window-java/src/androidTest/java/androidx/window/java/TestConsumer.kt
index cb53396..5fff062 100644
--- a/window/window-java/src/androidTest/java/androidx/window/java/TestConsumer.kt
+++ b/window/window-java/src/androidTest/java/androidx/window/java/TestConsumer.kt
@@ -22,8 +22,8 @@
 internal class TestConsumer<T> : Consumer<T> {
     private val values = mutableListOf<T>()
 
-    override fun accept(t: T) {
-        values.add(t)
+    override fun accept(value: T) {
+        values.add(value)
     }
 
     fun assertValueCount(count: Int) {
diff --git a/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt b/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt
index 69882ce..84da641 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt
@@ -150,8 +150,8 @@
             val callback: Consumer<WindowLayoutInfo>
         ) : Consumer<WindowLayoutInfo> {
 
-            override fun accept(t: WindowLayoutInfo?) {
-                executor.execute { callback.accept(t) }
+            override fun accept(value: WindowLayoutInfo) {
+                executor.execute { callback.accept(value) }
             }
         }
 
diff --git a/window/window/src/main/java/androidx/window/layout/adapter/extensions/MulticastConsumer.kt b/window/window/src/main/java/androidx/window/layout/adapter/extensions/MulticastConsumer.kt
index b57f88f..a5d2cc1 100644
--- a/window/window/src/main/java/androidx/window/layout/adapter/extensions/MulticastConsumer.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/extensions/MulticastConsumer.kt
@@ -39,8 +39,9 @@
 
     override fun accept(value: OEMWindowLayoutInfo) {
         multicastConsumerLock.withLock {
-            lastKnownValue = ExtensionsWindowLayoutInfoAdapter.translate(context, value)
-            registeredListeners.forEach { consumer -> consumer.accept(lastKnownValue) }
+            val newValue = ExtensionsWindowLayoutInfoAdapter.translate(context, value)
+            lastKnownValue = newValue
+            registeredListeners.forEach { consumer -> consumer.accept(newValue) }
         }
     }
 
diff --git a/window/window/src/testUtil/java/androidx/window/TestConsumer.kt b/window/window/src/testUtil/java/androidx/window/TestConsumer.kt
index 6fb559e..5c088e1 100644
--- a/window/window/src/testUtil/java/androidx/window/TestConsumer.kt
+++ b/window/window/src/testUtil/java/androidx/window/TestConsumer.kt
@@ -29,8 +29,8 @@
     /**
      * Add the value to the list of seen values.
      */
-    override fun accept(t: T) {
-        values.add(t)
+    override fun accept(value: T) {
+        values.add(value)
     }
 
     /**
diff --git a/work/work-runtime/build.gradle b/work/work-runtime/build.gradle
index 6f9cc9e..bc8d3bc 100644
--- a/work/work-runtime/build.gradle
+++ b/work/work-runtime/build.gradle
@@ -67,7 +67,7 @@
     implementation("androidx.core:core:1.12.0")
     implementation("androidx.room:room-ktx:2.6.1")
     implementation("androidx.concurrent:concurrent-futures:1.1.0")
-    api(projectOrArtifact(":annotation:annotation-experimental"))
+    api("androidx.annotation:annotation-experimental:1.4.0")
     api(libs.guavaListenableFuture)
     api("androidx.lifecycle:lifecycle-livedata:2.6.2")
     api("androidx.startup:startup-runtime:1.1.1")
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt b/work/work-runtime/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt
index d6ba0ad..3e3f75c 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt
+++ b/work/work-runtime/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,249 +17,147 @@
 package androidx.work
 
 import android.content.Context
-import android.util.Log
-import androidx.arch.core.executor.ArchTaskExecutor
-import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.LargeTest
-import androidx.test.filters.SmallTest
-import androidx.work.impl.WorkDatabase
+import androidx.test.filters.MediumTest
 import androidx.work.impl.WorkManagerImpl
-import androidx.work.impl.utils.SerialExecutorImpl
-import androidx.work.impl.utils.WorkProgressUpdater
-import androidx.work.impl.utils.futures.SettableFuture
-import androidx.work.impl.utils.taskexecutor.TaskExecutor
-import androidx.work.worker.ProgressUpdatingWorker
-import java.util.UUID
-import java.util.concurrent.Executor
-import kotlinx.coroutines.asCoroutineDispatcher
+import androidx.work.impl.constraints.trackers.Trackers
+import androidx.work.impl.testutils.TestConstraintTracker
+import androidx.work.impl.testutils.TrackingWorkerFactory
+import androidx.work.testutils.GreedyScheduler
+import androidx.work.testutils.TestEnv
+import androidx.work.testutils.WorkManager
+import androidx.work.testutils.awaitWorkerEnqueued
+import androidx.work.testutils.awaitWorkerFinished
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executors
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.runBlocking
-import org.hamcrest.CoreMatchers.instanceOf
-import org.hamcrest.CoreMatchers.`is`
-import org.hamcrest.CoreMatchers.nullValue
-import org.hamcrest.MatcherAssert.assertThat
-import org.junit.After
-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.Mockito.mock
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
 
 @RunWith(AndroidJUnit4::class)
-@SmallTest
+@MediumTest
 class CoroutineWorkerTest {
-
-    private lateinit var context: Context
-    private lateinit var configuration: Configuration
-    private lateinit var database: WorkDatabase
-    private lateinit var workManagerImpl: WorkManagerImpl
-    private lateinit var progressUpdater: ProgressUpdater
-    private lateinit var mForegroundUpdater: ForegroundUpdater
-
-    @Before
-    fun setUp() {
-        ArchTaskExecutor.getInstance()
-            .setDelegate(object : androidx.arch.core.executor.TaskExecutor() {
-                override fun executeOnDiskIO(runnable: Runnable) {
-                    runnable.run()
-                }
-
-                override fun postToMainThread(runnable: Runnable) {
-                    runnable.run()
-                }
-
-                override fun isMainThread(): Boolean {
-                    return true
-                }
-            })
-
-        context = ApplicationProvider.getApplicationContext()
-        configuration = Configuration.Builder()
-            .setExecutor(SynchronousExecutor())
-            .setMinimumLoggingLevel(Log.DEBUG)
-            .build()
-        workManagerImpl = WorkManagerImpl(
-            context, configuration,
-            InstantWorkTaskExecutor()
-        )
-        WorkManagerImpl.setDelegate(workManagerImpl)
-        database = workManagerImpl.workDatabase
-        // No op
-        progressUpdater = ProgressUpdater { _, _, _ ->
-            val future = SettableFuture.create<Void>()
-            future.set(null)
-            future
-        }
-        mForegroundUpdater = mock(ForegroundUpdater::class.java)
-    }
-
-    @After
-    fun tearDown() {
-        WorkManagerImpl.setDelegate(null)
-        ArchTaskExecutor.getInstance().setDelegate(null)
-    }
-
-    @Test
-    fun testCoroutineWorker_basicUsage() {
-        val workerFactory = DefaultWorkerFactory
-        val worker = workerFactory.createWorkerWithDefaultFallback(
-            context,
-            SynchronousCoroutineWorker::class.java.name,
-            WorkerParameters(workerFactory)
-        ) as SynchronousCoroutineWorker
-
-        assertThat(worker.job.isCompleted, `is`(false))
-
-        val future = worker.startWork()
-        val result = future.get()
-
-        assertThat(future.isDone, `is`(true))
-        assertThat(future.isCancelled, `is`(false))
-        assertThat(result, `is`(instanceOf(ListenableWorker.Result.Success::class.java)))
-        assertThat(
-            (result as ListenableWorker.Result.Success).outputData.getLong(
-                "output", 0L
-            ),
-            `is`(999L)
-        )
-    }
-
-    @Test
-    fun testCoroutineWorker_cancellingFutureCancelsJob() {
-        val workerFactory = DefaultWorkerFactory
-        val worker = workerFactory.createWorkerWithDefaultFallback(
-            context,
-            SynchronousCoroutineWorker::class.java.name,
-            WorkerParameters(workerFactory)
-        ) as SynchronousCoroutineWorker
-
-        assertThat(worker.job.isCancelled, `is`(false))
-        worker.future.cancel(true)
-        assertThat(worker.job.isCancelled, `is`(true))
-    }
-
-    @Test
-    @LargeTest
-    fun testProgressUpdates() {
-        val workerFactory = DefaultWorkerFactory
-        val progressUpdater = spy(WorkProgressUpdater(database, workManagerImpl.workTaskExecutor))
-        val workRequest = OneTimeWorkRequestBuilder<ProgressUpdatingWorker>().build()
-        database.workSpecDao().insertWorkSpec(workRequest.workSpec)
-        val worker = workerFactory.createWorkerWithDefaultFallback(
-            context,
-            ProgressUpdatingWorker::class.java.name,
-            WorkerParameters(
-                workerFactory,
-                workRequest.id,
-                progressUpdater,
-            )
-        ) as ProgressUpdatingWorker
-
-        runBlocking {
-            val result = worker.doWork()
-            val captor = ArgumentCaptor.forClass(Data::class.java)
-            verify(progressUpdater, times(2))
-                .updateProgress(
-                    any(Context::class.java),
-                    any(UUID::class.java),
-                    captor.capture()
-                )
-            assertThat(result, `is`(instanceOf(ListenableWorker.Result.Success::class.java)))
-            val recent = captor.allValues.lastOrNull()
-            assertThat(recent?.getInt(ProgressUpdatingWorker.Progress, 0), `is`(100))
-            val progress = database.workProgressDao().getProgressForWorkSpecId(workRequest.stringId)
-            assertThat(progress, nullValue())
-        }
-    }
-
-    @Test
-    @LargeTest
-    fun testProgressUpdatesForRetry() {
-        val workerFactory = DefaultWorkerFactory
-        val progressUpdater = spy(WorkProgressUpdater(database, workManagerImpl.workTaskExecutor))
-        val input = workDataOf(ProgressUpdatingWorker.Expected to "Retry")
-        val workRequest = OneTimeWorkRequestBuilder<ProgressUpdatingWorker>()
-            .setInputData(input)
-            .build()
-        database.workSpecDao().insertWorkSpec(workRequest.workSpec)
-        val worker = workerFactory.createWorkerWithDefaultFallback(
-            context,
-            ProgressUpdatingWorker::class.java.name,
-            WorkerParameters(
-                workerFactory,
-                workRequest.id,
-                progressUpdater,
-            )
-        ) as ProgressUpdatingWorker
-
-        runBlocking {
-            val result = worker.doWork()
-            val captor = ArgumentCaptor.forClass(Data::class.java)
-            verify(progressUpdater, times(2))
-                .updateProgress(
-                    any(Context::class.java),
-                    any(UUID::class.java),
-                    captor.capture()
-                )
-            assertThat(result, `is`(instanceOf(ListenableWorker.Result.Success::class.java)))
-            val recent = captor.allValues.lastOrNull()
-            assertThat(recent?.getInt(ProgressUpdatingWorker.Progress, 0), `is`(100))
-            val progress = database.workProgressDao().getProgressForWorkSpecId(workRequest.stringId)
-            assertThat(progress, nullValue())
-        }
-    }
-
-    class SynchronousExecutor : Executor {
-
-        override fun execute(command: Runnable) {
-            command.run()
-        }
-    }
-
-    class InstantWorkTaskExecutor : TaskExecutor {
-
-        private val mSynchronousExecutor = SynchronousExecutor()
-        private val mSerialExecutor = SerialExecutorImpl(mSynchronousExecutor)
-
-        override fun getMainThreadExecutor(): Executor {
-            return mSynchronousExecutor
-        }
-
-        override fun getSerialTaskExecutor(): SerialExecutorImpl {
-            return mSerialExecutor
-        }
-    }
-
-    class SynchronousCoroutineWorker(context: Context, params: WorkerParameters) :
-        CoroutineWorker(context, params) {
-
-        override suspend fun doWork(): Result {
-            return Result.success(workDataOf("output" to 999L))
-        }
-
-        @Deprecated(message = "use withContext(...) inside doWork() instead.")
-        override val coroutineContext = SynchronousExecutor().asCoroutineDispatcher()
-    }
-
-    fun WorkerParameters(
-        workerFactory: WorkerFactory,
-        id: UUID = UUID.randomUUID(),
-        progressUpdater: ProgressUpdater = this.progressUpdater,
-    ) = WorkerParameters(
-        id,
-        Data.EMPTY,
-        emptyList(),
-        WorkerParameters.RuntimeExtras(),
-        1,
-        1,
-        configuration.executor,
-        workManagerImpl.workTaskExecutor,
-        workerFactory,
-        progressUpdater,
-        mForegroundUpdater
+    val workerFactory = TrackingWorkerFactory()
+    val configuration =
+        Configuration.Builder().setWorkerFactory(workerFactory)
+            .setTaskExecutor(Executors.newSingleThreadExecutor()).build()
+    val env = TestEnv(configuration)
+    val taskExecutor = env.taskExecutor
+    val fakeChargingTracker = TestConstraintTracker(true, env.context, env.taskExecutor)
+    val trackers = Trackers(
+        context = env.context,
+        taskExecutor = env.taskExecutor,
+        batteryChargingTracker = fakeChargingTracker
     )
+    val greedyScheduler = GreedyScheduler(env, trackers)
+    val workManager = WorkManager(env, listOf(greedyScheduler), trackers)
+
+    init {
+        WorkManagerImpl.setDelegate(workManager)
+    }
+
+    @Test
+    fun testCoroutineWorker_basicUsage() = runBlocking {
+        val request = OneTimeWorkRequest.from(SuccessCoroutineWorker::class.java)
+        workManager.enqueue(request)
+        val workInfo = workManager.awaitWorkerFinished(request.id)
+        assertThat(workInfo.state).isEqualTo(WorkInfo.State.SUCCEEDED)
+        assertThat(workInfo.outputData.getLong("output", 0L)).isEqualTo(999L)
+    }
+
+    @Test
+    fun testCoroutineWorker_failingWorker() = runBlocking {
+        val request = OneTimeWorkRequest.from(ThrowingCoroutineWorker::class.java)
+        workManager.enqueue(request)
+        val workInfo = workManager.awaitWorkerFinished(request.id)
+        assertThat(workInfo.state).isEqualTo(WorkInfo.State.FAILED)
+    }
+
+    @Test
+    fun testCoroutineWorker_interruptionCancelsJob() = runBlocking {
+        val request =
+            OneTimeWorkRequest.Builder(CancellationCheckingWorker::class.java)
+                .setConstraints(Constraints(requiresCharging = true))
+                .build()
+        workManager.enqueue(request)
+        val worker = workerFactory.await(request.id) as CancellationCheckingWorker
+        worker.doWorkCalled.await()
+        fakeChargingTracker.constraintState = false
+        workManager.awaitWorkerEnqueued(request.id)
+        assertThat(worker.cancelled).isTrue()
+    }
+
+    @Test
+    fun testProgressUpdates() = runBlocking {
+        val request = OneTimeWorkRequest.from(ProgressUpdatingWorker::class.java)
+        workManager.enqueue(request)
+        val worker = workerFactory.await(request.id) as ProgressUpdatingWorker
+
+        val progress1 = workManager.getWorkInfoByIdFlow(request.id)
+            .first { it.progress.getInt("progress", 0) != 0 }.progress
+        assertThat(progress1.getInt("progress", 0)).isEqualTo(1)
+        worker.firstCheckPoint.complete(Unit)
+
+        val progress2 = workManager.getWorkInfoByIdFlow(request.id)
+            .first { it.progress.getInt("progress", 0) != 1 }.progress
+        assertThat(progress2.getInt("progress", 0)).isEqualTo(100)
+        worker.secondCheckPoint.complete(Unit)
+        val workInfo = workManager.awaitWorkerFinished(request.id)
+        assertThat(workInfo.state).isEqualTo(WorkInfo.State.SUCCEEDED)
+        assertThat(workInfo.progress).isEqualTo(Data.EMPTY)
+    }
+}
+
+class SuccessCoroutineWorker(
+    appContext: Context,
+    params: WorkerParameters
+) : CoroutineWorker(appContext, params) {
+    override suspend fun doWork(): Result = Result.success(workDataOf("output" to 999L))
+}
+
+class ThrowingCoroutineWorker(
+    appContext: Context,
+    params: WorkerParameters
+) : CoroutineWorker(appContext, params) {
+    override suspend fun doWork(): Result {
+        throw IllegalStateException("Failing worker")
+    }
+}
+
+class CancellationCheckingWorker(
+    appContext: Context,
+    params: WorkerParameters
+) : CoroutineWorker(appContext, params) {
+    var cancelled = false
+    val doWorkCalled = CompletableDeferred<Unit>()
+    override suspend fun doWork(): Result {
+        doWorkCalled.complete(Unit)
+        try {
+            // suspends forever
+            CompletableDeferred<Unit>().await()
+        } catch (c: CancellationException) {
+            cancelled = true
+            throw c
+        }
+        return Result.success()
+    }
+}
+
+class ProgressUpdatingWorker(
+    appContext: Context,
+    params: WorkerParameters
+) : CoroutineWorker(appContext, params) {
+    val firstCheckPoint = CompletableDeferred<Unit>()
+    val secondCheckPoint = CompletableDeferred<Unit>()
+
+    override suspend fun doWork(): Result {
+        setProgress(workDataOf("progress" to 1))
+        firstCheckPoint.await()
+        setProgress(workDataOf("progress" to 100))
+        secondCheckPoint.await()
+        return Result.success()
+    }
 }
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/testutils/WorkManagerExt.kt b/work/work-runtime/src/androidTest/java/androidx/work/testutils/WorkManagerExt.kt
new file mode 100644
index 0000000..84e2bf5
--- /dev/null
+++ b/work/work-runtime/src/androidTest/java/androidx/work/testutils/WorkManagerExt.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.work.testutils
+
+import androidx.work.WorkInfo
+import androidx.work.WorkManager
+import java.util.UUID
+import kotlinx.coroutines.flow.first
+
+suspend fun WorkManager.awaitWorkerFinished(id: UUID): WorkInfo = getWorkInfoByIdFlow(id).first {
+    it.state.isFinished
+}
+
+suspend fun WorkManager.awaitWorkerEnqueued(id: UUID): WorkInfo = getWorkInfoByIdFlow(id).first {
+    it.state == WorkInfo.State.ENQUEUED
+}
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/worker/ProgressUpdatingWorker.kt b/work/work-runtime/src/androidTest/java/androidx/work/worker/ProgressUpdatingWorker.kt
deleted file mode 100644
index 977be7d..0000000
--- a/work/work-runtime/src/androidTest/java/androidx/work/worker/ProgressUpdatingWorker.kt
+++ /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.
- */
-
-package androidx.work.worker
-
-import android.content.Context
-import androidx.work.CoroutineWorker
-import androidx.work.Data
-import androidx.work.WorkerParameters
-import kotlinx.coroutines.delay
-
-class ProgressUpdatingWorker(context: Context, parameters: WorkerParameters) :
-    CoroutineWorker(context, parameters) {
-
-    companion object {
-        const val Expected = "Expected"
-        const val Progress = "Progress"
-        private const val delayDuration = 1L
-    }
-
-    override suspend fun doWork(): Result {
-        val firstUpdate = Data.Builder().putInt(Progress, 10).build()
-        val lastUpdate = Data.Builder().putInt(Progress, 100).build()
-        val expected = inputData.getString(Expected)
-        setProgress(firstUpdate)
-        delay(delayDuration)
-        setProgress(lastUpdate)
-        return when (expected) {
-            "Failure" -> Result.failure()
-            "Retry" -> Result.retry()
-            else -> Result.success()
-        }
-    }
-}
diff --git a/work/work-runtime/src/main/java/androidx/work/CoroutineWorker.kt b/work/work-runtime/src/main/java/androidx/work/CoroutineWorker.kt
index 7cc80ef..d56aec9 100644
--- a/work/work-runtime/src/main/java/androidx/work/CoroutineWorker.kt
+++ b/work/work-runtime/src/main/java/androidx/work/CoroutineWorker.kt
@@ -17,13 +17,10 @@
 package androidx.work
 
 import android.content.Context
-import androidx.work.impl.utils.futures.SettableFuture
 import com.google.common.util.concurrent.ListenableFuture
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
 
 /**
  * A [ListenableWorker] implementation that provides interop with Kotlin Coroutines.  Override
@@ -40,20 +37,6 @@
     params: WorkerParameters
 ) : ListenableWorker(appContext, params) {
 
-    internal val job = Job()
-    internal val future: SettableFuture<Result> = SettableFuture.create()
-
-    init {
-        future.addListener(
-            Runnable {
-                if (future.isCancelled) {
-                    job.cancel()
-                }
-            },
-            taskExecutor.serialTaskExecutor
-        )
-    }
-
     /**
      * The coroutine context on which [doWork] will run. By default, this is [Dispatchers.Default].
      */
@@ -62,16 +45,7 @@
 
     @Suppress("DEPRECATION")
     public final override fun startWork(): ListenableFuture<Result> {
-        val coroutineScope = CoroutineScope(coroutineContext + job)
-        coroutineScope.launch {
-            try {
-                val result = doWork()
-                future.set(result)
-            } catch (t: Throwable) {
-                future.setException(t)
-            }
-        }
-        return future
+        return launchFuture(coroutineContext + Job()) { doWork() }
     }
 
     /**
@@ -127,17 +101,10 @@
 
     @Suppress("DEPRECATION")
     public final override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
-        val job = Job()
-        val scope = CoroutineScope(coroutineContext + job)
-        val jobFuture = JobListenableFuture<ForegroundInfo>(job)
-        scope.launch {
-            jobFuture.complete(getForegroundInfo())
-        }
-        return jobFuture
+        return launchFuture(coroutineContext + Job()) { getForegroundInfo() }
     }
 
     public final override fun onStopped() {
         super.onStopped()
-        future.cancel(false)
     }
 }
diff --git a/work/work-runtime/src/main/java/androidx/work/ListenableFuture.kt b/work/work-runtime/src/main/java/androidx/work/ListenableFuture.kt
index 98a79e8..3b897b1 100644
--- a/work/work-runtime/src/main/java/androidx/work/ListenableFuture.kt
+++ b/work/work-runtime/src/main/java/androidx/work/ListenableFuture.kt
@@ -19,16 +19,19 @@
 package androidx.work
 
 import androidx.annotation.RestrictTo
-import androidx.concurrent.futures.CallbackToFutureAdapter
-import androidx.work.impl.utils.futures.SettableFuture
+import androidx.concurrent.futures.CallbackToFutureAdapter.getFuture
 import com.google.common.util.concurrent.ListenableFuture
-import java.util.concurrent.CancellationException
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicBoolean
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.coroutines.resume
 import kotlin.coroutines.resumeWithException
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.suspendCancellableCoroutine
 
 /**
@@ -49,7 +52,7 @@
     }
     return suspendCancellableCoroutine { cancellableContinuation ->
         addListener(
-            Runnable {
+            {
                 try {
                     cancellableContinuation.resume(get())
                 } catch (throwable: Throwable) {
@@ -69,39 +72,34 @@
     }
 }
 
-/**
- * A special [Job] to [ListenableFuture] wrapper.
- */
-internal class JobListenableFuture<R>(
-    private val job: Job,
-    private val underlying: SettableFuture<R> = SettableFuture.create()
-) : ListenableFuture<R> by underlying {
-
-    public fun complete(result: R) {
-        underlying.set(result)
-    }
-
-    init {
-        job.invokeOnCompletion { throwable: Throwable? ->
-            when (throwable) {
-                null -> require(underlying.isDone)
-                is CancellationException -> underlying.cancel(true)
-                else -> underlying.setException(throwable.cause ?: throwable)
-            }
+internal fun <T> launchFuture(
+    context: CoroutineContext = EmptyCoroutineContext,
+    block: suspend CoroutineScope.() -> T,
+): ListenableFuture<T> = getFuture { completer ->
+    val job = context[Job]
+    completer.addCancellationListener({ job?.cancel() }, DirectExecutor.INSTANCE)
+    CoroutineScope(context).launch {
+        try {
+            val result = block()
+            completer.set(result)
+        } catch (_: CancellationException) {
+            completer.setCancelled()
+        } catch (throwable: Throwable) {
+            completer.setException(throwable)
         }
     }
 }
 
 internal fun <V> Executor.executeAsync(debugTag: String, block: () -> V): ListenableFuture<V> =
-    CallbackToFutureAdapter.getFuture { completer ->
+    getFuture { completer ->
         val cancelled = AtomicBoolean(false)
         completer.addCancellationListener({ cancelled.set(true) }, DirectExecutor.INSTANCE)
         execute {
             if (cancelled.get()) return@execute
             try {
                 completer.set(block())
-            } catch (t: Throwable) {
-                completer.setException(t)
+            } catch (throwable: Throwable) {
+                completer.setException(throwable)
             }
         }
         debugTag