Merge "Closeable Recomposer" into androidx-main
diff --git a/activity/activity-ktx/build.gradle b/activity/activity-ktx/build.gradle
index 4175155..e754ba6 100644
--- a/activity/activity-ktx/build.gradle
+++ b/activity/activity-ktx/build.gradle
@@ -30,11 +30,11 @@
     api("androidx.core:core-ktx:1.1.0") {
         because 'Mirror activity dependency graph for -ktx artifacts'
     }
-    api(projectOrArtifact(":lifecycle:lifecycle-runtime-ktx")) {
+    api("androidx.lifecycle:lifecycle-runtime-ktx:2.3.0") {
         because 'Mirror activity dependency graph for -ktx artifacts'
     }
-    api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-ktx"))
-    api(projectOrArtifact(":savedstate:savedstate-ktx")) {
+    api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0")
+    api("androidx.savedstate:savedstate-ktx:1.1.0") {
         because 'Mirror activity dependency graph for -ktx artifacts'
     }
     api(KOTLIN_STDLIB)
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index 1159550..864eb0f 100644
--- a/activity/activity/build.gradle
+++ b/activity/activity/build.gradle
@@ -23,10 +23,10 @@
     api("androidx.annotation:annotation:1.1.0")
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.core:core:1.1.0")
-    api(projectOrArtifact(":lifecycle:lifecycle-runtime"))
-    api(projectOrArtifact(":lifecycle:lifecycle-viewmodel"))
-    api(projectOrArtifact(":savedstate:savedstate"))
-    api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-savedstate"))
+    api("androidx.lifecycle:lifecycle-runtime:2.3.0")
+    api("androidx.lifecycle:lifecycle-viewmodel:2.3.0")
+    api("androidx.savedstate:savedstate:1.1.0")
+    api("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0")
     implementation("androidx.tracing:tracing:1.0.0")
 
     androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index 7b5c37e..05e1d78 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -19,9 +19,9 @@
     api("androidx.fragment:fragment:1.3.0-rc01")
     api(project(":appcompat:appcompat-resources"))
     api("androidx.drawerlayout:drawerlayout:1.0.0")
-    implementation("androidx.lifecycle:lifecycle-runtime:2.3.0-rc01")
-    implementation("androidx.lifecycle:lifecycle-viewmodel:2.3.0-rc01")
-    api("androidx.savedstate:savedstate:1.1.0-rc01")
+    implementation("androidx.lifecycle:lifecycle-runtime:2.3.0")
+    implementation("androidx.lifecycle:lifecycle-viewmodel:2.3.0")
+    api("androidx.savedstate:savedstate:1.1.0")
 
     androidTestImplementation(KOTLIN_STDLIB)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/appcompat/integration-tests/receive-content-testapp/build.gradle b/appcompat/integration-tests/receive-content-testapp/build.gradle
new file mode 100644
index 0000000..de5f8c7
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/build.gradle
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 16
+    }
+}
+
+dependencies {
+    api("androidx.annotation:annotation:1.1.0")
+    implementation(project(":appcompat:appcompat"))
+    implementation(CONSTRAINT_LAYOUT, { transitive = true })
+    implementation(GUAVA_ANDROID)
+
+    androidTestImplementation(ANDROIDX_TEST_CORE)
+    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(ANDROIDX_TEST_RULES)
+    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+    implementation(ESPRESSO_IDLING_RESOURCE)
+    implementation(TRUTH)
+}
diff --git a/appcompat/integration-tests/receive-content-testapp/lint-baseline.xml b/appcompat/integration-tests/receive-content-testapp/lint-baseline.xml
new file mode 100644
index 0000000..8f1aa4b
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.2.0-beta02" client="gradle" variant="debug" version="4.2.0-beta02">
+
+</issues>
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/AndroidManifest.xml b/appcompat/integration-tests/receive-content-testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6b74a84
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.appcompat.demo.receivecontent">
+
+    <application
+        android:allowBackup="true"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme">
+        <activity
+            android:name="androidx.appcompat.demo.receivecontent.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/Logcat.java b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/Logcat.java
new file mode 100644
index 0000000..835059f
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/Logcat.java
@@ -0,0 +1,23 @@
+/*
+ * 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.appcompat.demo.receivecontent;
+
+final class Logcat {
+    private Logcat() {}
+
+    public static final String TAG = "ReceiveContentDemo";
+}
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MainActivity.java b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MainActivity.java
new file mode 100644
index 0000000..9b2286c
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MainActivity.java
@@ -0,0 +1,37 @@
+/*
+ * 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.appcompat.demo.receivecontent;
+
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.AppCompatEditText;
+import androidx.core.view.ViewCompat;
+
+/** Main activity for the app. */
+public class MainActivity extends AppCompatActivity {
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        AppCompatEditText textInput = findViewById(R.id.text_input);
+        ViewCompat.setOnReceiveContentListener(textInput,
+                MyReceiver.SUPPORTED_MIME_TYPES, new MyReceiver());
+    }
+}
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyExecutors.java b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyExecutors.java
new file mode 100644
index 0000000..d3a32ea
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyExecutors.java
@@ -0,0 +1,42 @@
+/*
+ * 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.appcompat.demo.receivecontent;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+final class MyExecutors {
+    private MyExecutors() {}
+
+    private static final Handler MAIN = new Handler(Looper.getMainLooper());
+    private static final ExecutorService BG = Executors.newSingleThreadExecutor();
+
+    @NonNull
+    public static Handler main() {
+        return MAIN;
+    }
+
+    @NonNull
+    public static ExecutorService bg() {
+        return BG;
+    }
+}
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyReceiver.java b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyReceiver.java
new file mode 100644
index 0000000..1b92d5c
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyReceiver.java
@@ -0,0 +1,80 @@
+/*
+ * 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.appcompat.demo.receivecontent;
+
+import android.content.ClipData;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.net.Uri;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.view.ContentInfoCompat;
+import androidx.core.view.OnReceiveContentListener;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Sample {@link OnReceiveContentListener} implementation that accepts all URIs, and delegates
+ * handling for all other content to the platform.
+ */
+public class MyReceiver implements OnReceiveContentListener {
+    public static final String[] SUPPORTED_MIME_TYPES = new String[]{"image/*"};
+
+    @Nullable
+    @Override
+    public ContentInfoCompat onReceiveContent(@NonNull View view,
+            @NonNull ContentInfoCompat contentInfo) {
+        Pair<ContentInfoCompat, ContentInfoCompat> split = contentInfo.partition(
+                item -> item.getUri() != null);
+        ContentInfoCompat uriContent = split.first;
+        ContentInfoCompat remaining = split.second;
+        if (uriContent != null) {
+            ClipData clip = uriContent.getClip();
+            for (int i = 0; i < clip.getItemCount(); i++) {
+                receive(view, clip.getItemAt(i).getUri());
+            }
+        }
+        return remaining;
+    }
+
+    private static void receive(@NonNull View view, @NonNull Uri contentUri) {
+        final Context applicationContext = view.getContext().getApplicationContext();
+        MyExecutors.bg().execute(() -> {
+            ContentResolver contentResolver = applicationContext.getContentResolver();
+            String mimeType = contentResolver.getType(contentUri);
+            long lengthBytes;
+            try {
+                AssetFileDescriptor fd = contentResolver.openAssetFileDescriptor(contentUri, "r");
+                lengthBytes = fd.getLength();
+            } catch (FileNotFoundException e) {
+                Log.e(Logcat.TAG, "Error opening content URI: " + contentUri, e);
+                return;
+            }
+            String msg = "Received " + mimeType + " (" + lengthBytes + " bytes): " + contentUri;
+            Log.i(Logcat.TAG, msg);
+            MyExecutors.main().post(() -> {
+                Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG).show();
+            });
+        });
+    }
+}
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/res/layout/activity_main.xml b/appcompat/integration-tests/receive-content-testapp/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..6a820f3
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/res/layout/activity_main.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/layout_root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+
+    <androidx.appcompat.widget.AppCompatEditText
+        android:id="@+id/text_input"
+        android:inputType="text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginVertical="2dp"
+        android:layout_marginHorizontal="8dp"
+        android:gravity="top"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        android:text="@string/text_input_default_text" />
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/res/values/strings.xml b/appcompat/integration-tests/receive-content-testapp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..bb9fdb58
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <string name="app_name">Receive Content AndroidX Demo</string>
+    <string name="text_input_default_text">Hello world</string>
+</resources>
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/res/values/styles.xml b/appcompat/integration-tests/receive-content-testapp/src/main/res/values/styles.xml
new file mode 100644
index 0000000..a948e6f
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <style name="AppTheme" parent="Theme.AppCompat.Light">
+    </style>
+</resources>
diff --git a/benchmark/macro/src/androidTest/java/androidx/benchmark/macro/test/StartupTimingMetricTest.kt b/benchmark/macro/src/androidTest/java/androidx/benchmark/macro/test/StartupTimingMetricTest.kt
index 2970e98..6bbf570 100644
--- a/benchmark/macro/src/androidTest/java/androidx/benchmark/macro/test/StartupTimingMetricTest.kt
+++ b/benchmark/macro/src/androidTest/java/androidx/benchmark/macro/test/StartupTimingMetricTest.kt
@@ -26,6 +26,7 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import org.junit.Assert.assertEquals
+import org.junit.Assume.assumeFalse
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -35,9 +36,7 @@
     @LargeTest
     @Test
     fun noResults() {
-        if (Build.SUPPORTED_64_BIT_ABIS.isEmpty()) {
-            return
-        }
+        assumeFalse(Build.SUPPORTED_64_BIT_ABIS.isEmpty())
 
         val packageName = "fake.package.fiction.nostartups"
         val metrics = measureStartup(packageName) {
@@ -49,9 +48,7 @@
     @LargeTest
     @Test
     fun validateStartup() {
-        if (Build.SUPPORTED_64_BIT_ABIS.isEmpty()) {
-            return
-        }
+        assumeFalse(Build.SUPPORTED_64_BIT_ABIS.isEmpty())
 
         val packageName = "androidx.benchmark.integration.macrobenchmark.target"
         val scope = MacrobenchmarkScope(packageName = packageName, launchWithClearTask = true)
@@ -80,13 +77,8 @@
         metrics = listOf(metric)
     )
     metric.configure(config)
-    return wrapper.captureTrace(packageName, iteration = 1) { tracePath ->
-        try {
-            metric.start()
-            measureBlock()
-            metric.getMetrics(packageName, tracePath)
-        } finally {
-            metric.stop()
-        }
-    }
+    wrapper.start()
+    measureBlock()
+    val tracePath = wrapper.stop(packageName, 1)!!
+    return metric.getMetrics(packageName, tracePath)
 }
diff --git a/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index de7defa..1424c88 100644
--- a/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -141,23 +141,24 @@
         val metricResults = List(config.iterations) { iteration ->
             setupBlock(scope, isFirstRun)
             isFirstRun = false
-            perfettoCollector.captureTrace(uniqueName, iteration) { tracePath ->
-                try {
-                    config.metrics.forEach {
-                        it.start()
-                    }
-                    measureBlock(scope)
-                } finally {
-                    config.metrics.forEach {
-                        it.stop()
-                    }
+            perfettoCollector.start()
+
+            try {
+                config.metrics.forEach {
+                    it.start()
                 }
-                config.metrics
-                    // capture list of Map<String,Long> per metric
-                    .map { it.getMetrics(config.packageName, tracePath) }
-                    // merge into one map
-                    .reduce { sum, element -> sum + element }
+                measureBlock(scope)
+            } finally {
+                config.metrics.forEach {
+                    it.stop()
+                }
             }
+            val tracePath = perfettoCollector.stop(uniqueName, iteration)
+            config.metrics
+                // capture list of Map<String,Long> per metric
+                .map { it.getMetrics(config.packageName, tracePath!!) }
+                // merge into one map
+                .reduce { sum, element -> sum + element }
         }.mergeToMetricResults()
 
         InstrumentationResults.instrumentationReport {
diff --git a/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt b/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt
index b093ea1..6892aee 100644
--- a/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt
+++ b/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt
@@ -147,10 +147,10 @@
         val instrumentation = InstrumentationRegistry.getInstrumentation()
         device = instrumentation.device()
         parser = PerfettoTraceParser()
+        parser.copyTraceProcessorShell()
     }
 
     override fun start() {
-        parser.copyTraceProcessorShell()
     }
 
     override fun stop() {
@@ -171,7 +171,7 @@
     }
 
     companion object {
-        private const val TAG = "PerfettoMetric"
+        private const val TAG = "StartupTimingMetric"
         private const val METRICS = "android_startup"
     }
 }
diff --git a/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt b/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
index 361ffac..35a28e5 100644
--- a/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
+++ b/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
@@ -19,7 +19,6 @@
 import android.os.Build
 import android.util.Log
 import androidx.benchmark.perfetto.PerfettoCapture
-import androidx.benchmark.perfetto.PerfettoHelper
 import androidx.benchmark.perfetto.destinationPath
 import androidx.benchmark.perfetto.reportAdditionalFileToCopy
 
@@ -34,20 +33,7 @@
         }
     }
 
-    fun <T> captureTrace(
-        benchmarkName: String,
-        iteration: Int,
-        block: (String) -> T
-    ): T {
-        try {
-            start()
-            return block(PerfettoHelper.getPerfettoTmpOutputFilePath())
-        } finally {
-            stop(benchmarkName, iteration)
-        }
-    }
-
-    private fun start(): Boolean {
+    fun start(): Boolean {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             Log.d(TAG, "Recording perfetto trace")
             capture?.start()
@@ -55,7 +41,7 @@
         return true
     }
 
-    private fun stop(benchmarkName: String, iteration: Int): Boolean {
+    fun stop(benchmarkName: String, iteration: Int): String? {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             val iterString = iteration.toString().padStart(3, '0')
             // NOTE: macrobench still using legacy .trace name until
@@ -64,7 +50,8 @@
             val destination = destinationPath(traceName).absolutePath
             capture?.stop(destination)
             reportAdditionalFileToCopy("perfetto_trace_$iterString", destination)
+            return destination
         }
-        return true
+        return null
     }
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 58a9c20..face0e9 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -106,7 +106,7 @@
 const val REACTIVE_STREAMS = "org.reactivestreams:reactive-streams:1.0.0"
 const val RX_JAVA = "io.reactivex.rxjava2:rxjava:2.2.9"
 const val RX_JAVA3 = "io.reactivex.rxjava3:rxjava:3.0.0"
-val SKIKO_VERSION = System.getenv("SKIKO_VERSION") ?: "0.1.21"
+val SKIKO_VERSION = System.getenv("SKIKO_VERSION") ?: "0.2.4"
 val SKIKO = "org.jetbrains.skiko:skiko-jvm:$SKIKO_VERSION"
 val SKIKO_LINUX_X64 = "org.jetbrains.skiko:skiko-jvm-runtime-linux-x64:$SKIKO_VERSION"
 val SKIKO_MACOS_X64 = "org.jetbrains.skiko:skiko-jvm-runtime-macos-x64:$SKIKO_VERSION"
diff --git a/busytown/impl/build.sh b/busytown/impl/build.sh
index e70e63b..ff23e00 100755
--- a/busytown/impl/build.sh
+++ b/busytown/impl/build.sh
@@ -47,18 +47,7 @@
 # --no-watch-fs disables file system watch, because it does not work on busytown
 # due to our builders using OS that is too old.
 run $PROJECTS_ARG OUT_DIR=$OUT_DIR DIST_DIR=$DIST_DIR ANDROID_HOME=../../prebuilts/fullsdk-linux \
-    ./gradlew \
-    --stacktrace \
-    -Pandroidx.summarizeStderr \
-    -Pandroidx.allWarningsAsErrors \
-    -Pandroidx.coverageEnabled=true \
-    -Pandroidx.enableAffectedModuleDetection \
-    -Pandroidx.validateNoUnrecognizedMessages \
-    -PverifyUpToDate \
-    --no-watch-fs \
-    --no-daemon \
-    --offline \
-    "$@"
+    ./gradlew --ci "$@"
 
 # check that no unexpected modifications were made to the source repository, such as new cache directories
 DIST_DIR=$DIST_DIR $SCRIPT_DIR/verify_no_caches_in_source_repo.sh $BUILD_START_MARKER
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplForceOpenCameraTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplForceOpenCameraTest.kt
new file mode 100644
index 0000000..d30d2f2
--- /dev/null
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplForceOpenCameraTest.kt
@@ -0,0 +1,243 @@
+/*
+ * 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.camera.camera2.internal
+
+import android.content.Context
+import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraManager
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import androidx.camera.camera2.internal.compat.CameraManagerCompat
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.Logger
+import androidx.camera.core.impl.CameraInternal.State
+import androidx.camera.core.impl.CameraStateRegistry
+import androidx.camera.core.impl.Observable.Observer
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.testing.CameraUtil
+import androidx.core.os.HandlerCompat
+import androidx.test.core.app.ApplicationProvider
+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.After
+import org.junit.AfterClass
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+import org.junit.runner.RunWith
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
+
+/**
+ * Tests [Camera2CameraImpl]'s force opening camera behavior.
+ *
+ * The test opens a camera with Camera2 (using [CameraDevice]), then attempts to open the same
+ * camera with CameraX (using [Camera2CameraImpl]).
+ *
+ * Camera opening behavior is different in API levels 21/22 compared to API levels 23 and above.
+ * In API levels 21 and 22, a second camera client cannot open a camera until the first client
+ * closes it, whereas in later API levels, the camera service steals the camera away from a
+ * client when another one with the same or a higher priority attempts to open it.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+public class Camera2CameraImplForceOpenCameraTest {
+
+    @get:Rule
+    public val mCameraRule: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
+
+    private lateinit var mCameraId: String
+    private lateinit var mCamera2Camera: CameraDevice
+    private val mCameraXCameraToStateObserver = mutableMapOf<Camera2CameraImpl, Observer<State>>()
+
+    @Before
+    public fun getCameraId() {
+        val cameraId = CameraUtil.getCameraIdWithLensFacing(CameraSelector.LENS_FACING_BACK)
+        assumeFalse("Device doesn't have a back facing camera", cameraId == null)
+        mCameraId = cameraId!!
+    }
+
+    @After
+    public fun releaseCameraResources() {
+        if (::mCamera2Camera.isInitialized) {
+            mCamera2Camera.close()
+        }
+        for (entry in mCameraXCameraToStateObserver) {
+            releaseCameraXCameraResource(entry)
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+    @Test
+    public fun openCameraImmediately_ifCameraCanBeStolen() {
+        // Open the camera with Camera2
+        val camera2CameraOpen = openCamera_camera2(mCameraId)
+        camera2CameraOpen.await()
+
+        // Open the camera with CameraX, this steals it away from Camera2
+        val cameraXCameraOpen = openCamera_cameraX(mCameraId)
+        cameraXCameraOpen.await()
+    }
+
+    @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.LOLLIPOP_MR1)
+    @Test
+    public fun openCameraWhenAvailable_ifCameraCannotBeStolen() {
+        // Open the camera with Camera2
+        val camera2CameraOpen = openCamera_camera2(mCameraId)
+        camera2CameraOpen.await()
+
+        // Attempt to open the camera with CameraX, this will fail
+        val cameraXCameraOpen = openCamera_cameraX(mCameraId)
+        assertThat(cameraXCameraOpen.timesOutWhileWaiting()).isTrue()
+
+        // Close the camera with Camera2, and wait for it to be opened with CameraX
+        mCamera2Camera.close()
+        cameraXCameraOpen.await()
+    }
+
+    @Test
+    public fun openCameraWhenAvailable_ifMaxAllowedOpenedCamerasReached() {
+        // Open the camera with CameraX
+        val cameraOpen1 = openCamera_cameraX(mCameraId)
+        cameraOpen1.await()
+
+        // Open the camera again with CameraX
+        val cameraOpen2 = openCamera_cameraX(mCameraId)
+        assertThat(cameraOpen2.timesOutWhileWaiting()).isTrue()
+
+        // Close the first camera instance, and wait for it to be opened with the second instance
+        releaseCameraXCameraResource(mCameraXCameraToStateObserver.entries.first())
+        cameraOpen2.await()
+    }
+
+    private fun openCamera_camera2(cameraId: String): Semaphore {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
+        val cameraOpenSemaphore = Semaphore(0)
+        cameraManager.openCamera(
+            cameraId,
+            object : CameraDevice.StateCallback() {
+                override fun onOpened(camera: CameraDevice) {
+                    Logger.d(TAG, "Camera2: Camera open")
+                    mCamera2Camera = camera
+                    cameraOpenSemaphore.release()
+                }
+
+                override fun onDisconnected(camera: CameraDevice) {
+                    Logger.d(TAG, "Camera2: Camera disconnected")
+                    mCamera2Camera = camera
+                }
+
+                override fun onError(camera: CameraDevice, error: Int) {
+                    Logger.d(TAG, "Camera2: Camera error $error")
+                    mCamera2Camera = camera
+                }
+            },
+            sCameraHandler
+        )
+        return cameraOpenSemaphore
+    }
+
+    private fun openCamera_cameraX(cameraId: String): Semaphore {
+        // Build camera manager wrapper
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val cameraManagerCompat = CameraManagerCompat.from(context)
+
+        // Build camera info from cameraId
+        val camera2CameraInfo = Camera2CameraInfoImpl(
+            cameraId,
+            cameraManagerCompat.getCameraCharacteristicsCompat(cameraId)
+        )
+
+        // Initialize camera instance
+        val camera = Camera2CameraImpl(
+            cameraManagerCompat,
+            cameraId,
+            camera2CameraInfo,
+            mCameraRegistry,
+            sCameraExecutor,
+            sCameraHandler
+        )
+
+        // Open the camera
+        camera.open()
+        val cameraOpenSemaphore = Semaphore(0)
+        val stateObserver = object : Observer<State> {
+            override fun onNewData(value: State?) {
+                if (value == State.OPEN) {
+                    Logger.d(TAG, "CameraX: Camera open")
+                    cameraOpenSemaphore.release()
+                }
+            }
+
+            override fun onError(throwable: Throwable) {
+                Logger.e(TAG, "CameraX: Camera error $throwable")
+            }
+        }
+        camera.cameraState.addObserver(sCameraExecutor, stateObserver)
+        mCameraXCameraToStateObserver[camera] = stateObserver
+        return cameraOpenSemaphore
+    }
+
+    private fun releaseCameraXCameraResource(
+        entry: MutableMap.MutableEntry<Camera2CameraImpl, Observer<State>>
+    ) {
+        entry.key.cameraState.removeObserver(entry.value)
+        entry.key.release().get()
+    }
+
+    private fun Semaphore.await() {
+        assertThat(tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+    }
+
+    private fun Semaphore.timesOutWhileWaiting(): Boolean {
+        val acquired = tryAcquire(5, TimeUnit.SECONDS)
+        return !acquired
+    }
+
+    public companion object {
+        private const val TAG = "ForceOpenCameraTest"
+
+        private lateinit var sCameraHandlerThread: HandlerThread
+        private lateinit var sCameraHandler: Handler
+        private lateinit var sCameraExecutor: ExecutorService
+        private lateinit var mCameraRegistry: CameraStateRegistry
+
+        @BeforeClass
+        @JvmStatic
+        public fun classSetup() {
+            sCameraHandlerThread = HandlerThread("cameraThread")
+            sCameraHandlerThread.start()
+            sCameraHandler = HandlerCompat.createAsync(sCameraHandlerThread.looper)
+            sCameraExecutor = CameraXExecutors.newHandlerExecutor(sCameraHandler)
+            mCameraRegistry = CameraStateRegistry(1)
+        }
+
+        @AfterClass
+        @JvmStatic
+        public fun classTeardown() {
+            sCameraHandlerThread.quitSafely()
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 67e08ae..d476693 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -233,7 +233,7 @@
     private void openInternal() {
         switch (mState) {
             case INITIALIZED:
-                openCameraDevice(/*fromScheduledCameraReopen=*/false);
+                tryForceOpenCameraDevice();
                 break;
             case CLOSING:
                 setState(InternalState.REOPENING);
@@ -656,7 +656,7 @@
         }
     }
 
-    // Attempts to make use attach if they are not already attached.
+    /** Attempts to attach use cases if they are not already attached. */
     @ExecutedBy("mExecutor")
     private void tryAttachUseCases(@NonNull Collection<UseCase> toAdd) {
         final boolean attachUseCaseFromEmpty =
@@ -875,28 +875,64 @@
         return mCameraInfoInternal;
     }
 
-    /** Opens the camera device */
-    // TODO(b/124268878): Handle SecurityException and require permission in manifest.
-    @SuppressLint("MissingPermission")
+    /**
+     * Attempts to force open the camera device, which may result in stealing it from a lower
+     * priority client. This should only happen if another client doesn't close the camera when
+     * it should, e.g. when its process is moved to the background.
+     */
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     @ExecutedBy("mExecutor")
-    void openCameraDevice(boolean fromScheduledCameraReopen) {
+    void tryForceOpenCameraDevice() {
+        debugLog("Attempting to force open the camera.");
+        final boolean shouldTryOpenCamera = mCameraStateRegistry.tryOpenCamera(this);
+        if (!shouldTryOpenCamera) {
+            debugLog("No cameras available. Waiting for available camera before opening camera.");
+            setState(InternalState.PENDING_OPEN);
+            return;
+        }
+        openCameraDevice(false);
+    }
+
+    /**
+     * Attempts to open the camera device. Unlike {@link #tryForceOpenCameraDevice()}, this method
+     * does not steal the camera away from other clients.
+     *
+     * @param fromScheduledCameraReopen True if the attempt to open the camera originated from a
+     *                                  {@linkplain StateCallback.ScheduledReopen scheduled
+     *                                  reopen of the camera}. False otherwise.
+     */
+    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+    @ExecutedBy("mExecutor")
+    void tryOpenCameraDevice(boolean fromScheduledCameraReopen) {
+        debugLog("Attempting to open the camera.");
+        final boolean shouldTryOpenCamera =
+                mCameraAvailability.isCameraAvailable() && mCameraStateRegistry.tryOpenCamera(this);
+        if (!shouldTryOpenCamera) {
+            debugLog("No cameras available. Waiting for available camera before opening camera.");
+            setState(InternalState.PENDING_OPEN);
+            return;
+        }
+        openCameraDevice(fromScheduledCameraReopen);
+    }
+
+    /**
+     * Opens the camera device.
+     *
+     * @param fromScheduledCameraReopen True if the attempt to open the camera originated from a
+     *                                  {@linkplain StateCallback.ScheduledReopen scheduled
+     *                                  reopen of the camera}. False otherwise.
+     */
+    // TODO(b/124268878): Handle SecurityException and require permission in manifest.
+    @SuppressLint("MissingPermission")
+    @ExecutedBy("mExecutor")
+    private void openCameraDevice(boolean fromScheduledCameraReopen) {
         if (!fromScheduledCameraReopen) {
             mStateCallback.resetReopenMonitor();
         }
         mStateCallback.cancelScheduledReopen();
 
-        // Check that we have an available camera to open here before attempting
-        // to open the camera again.
-        if (!mCameraAvailability.isCameraAvailable() || !mCameraStateRegistry.tryOpenCamera(this)) {
-            debugLog("No cameras available. Waiting for available camera before opening camera.");
-            setState(InternalState.PENDING_OPEN);
-            return;
-        } else {
-            setState(InternalState.OPENING);
-        }
-
         debugLog("Opening camera.");
+        setState(InternalState.OPENING);
 
         try {
             mCameraManager.openCamera(mCameraInfoInternal.getCameraId(), mExecutor,
@@ -1346,7 +1382,7 @@
                                 mCameraDeviceError));
                         scheduleCameraReopen();
                     } else {
-                        openCameraDevice(/*fromScheduledCameraReopen=*/false);
+                        tryOpenCameraDevice(/*fromScheduledCameraReopen=*/false);
                     }
                     break;
                 default:
@@ -1524,7 +1560,7 @@
                     // this is still the scheduled reopen.
                     if (!mCancelled) {
                         Preconditions.checkState(mState == InternalState.REOPENING);
-                        openCameraDevice(/*fromScheduledCameraReopen=*/true);
+                        tryOpenCameraDevice(/*fromScheduledCameraReopen=*/true);
                     }
                 });
             }
@@ -1608,7 +1644,7 @@
             mCameraAvailable = true;
 
             if (mState == InternalState.PENDING_OPEN) {
-                openCameraDevice(/*fromScheduledCameraReopen=*/false);
+                tryOpenCameraDevice(/*fromScheduledCameraReopen=*/false);
             }
         }
 
@@ -1627,7 +1663,7 @@
         @ExecutedBy("mExecutor")
         public void onOpenAvailable() {
             if (mState == InternalState.PENDING_OPEN) {
-                openCameraDevice(/*fromScheduledCameraReopen=*/false);
+                tryForceOpenCameraDevice();
             }
         }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
index 2d630a2..9f0cacf 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
@@ -266,7 +266,7 @@
         // Do not use FutureChain to chain the initFuture, because FutureChain.transformAsync()
         // will not propagate if the input initFuture is failed. We want to always
         // shutdown the CameraX instance to ensure that resources are freed.
-        sShutdownFuture = CallbackToFutureAdapter.getFuture(
+        sShutdownFuture = Futures.nonCancellationPropagating(CallbackToFutureAdapter.getFuture(
                 completer -> {
                     synchronized (INSTANCE_LOCK) {
                         // Wait initialize complete
@@ -276,7 +276,7 @@
                         }, CameraXExecutors.directExecutor());
                         return "CameraX shutdown";
                     }
-                });
+                }));
         return sShutdownFuture;
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraStateRegistry.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraStateRegistry.java
index e8cfe7c..0f3e919 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraStateRegistry.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraStateRegistry.java
@@ -37,8 +37,8 @@
  * A registry that tracks the state of cameras.
  *
  * <p>The registry tracks internally how many cameras are open and how many are available to open.
- * Cameras that are in a PENDING_OPEN can be be notified when there is a slot available to
- * open a camera.
+ * Cameras that are in a {@link CameraInternal.State#PENDING_OPEN} state can be notified when
+ * there is a slot available to open a camera.
  */
 public final class CameraStateRegistry {
     private static final String TAG = "CameraStateRegistry";
@@ -56,7 +56,7 @@
     /**
      * Creates a new registry with a limit of {@code maxAllowedOpenCameras} allowed to be opened.
      *
-     * @param maxAllowedOpenedCameras The limit of number of simultaneous open cameras.
+     * @param maxAllowedOpenedCameras The limit for number of simultaneous open cameras.
      */
     public CameraStateRegistry(int maxAllowedOpenedCameras) {
         mMaxAllowedOpenedCameras = maxAllowedOpenedCameras;
@@ -74,7 +74,7 @@
      * <p>Before attempting to open a camera, {@link #tryOpenCamera(Camera)} must be called and
      * callers should only continue to open the camera if it returns {@code true}.
      *
-     * <p>Cameras will be automatically unregistered when the are marked as being in a
+     * <p>Cameras will be automatically unregistered when they are marked as being in a
      * {@link CameraInternal.State#RELEASED} state.
      *
      * @param camera The camera to register.
@@ -203,14 +203,14 @@
             @NonNull CameraInternal.State state) {
         CameraInternal.State previousState = Preconditions.checkNotNull(mCameraStates.get(camera),
                 "Cannot update state of camera which has not yet been registered. Register with "
-                        + "CameraAvailabilityRegistry.registerCamera()").setState(state);
+                        + "CameraStateRegistry.registerCamera()").setState(state);
 
         if (state == CameraInternal.State.OPENING) {
             // A camera should only enter an OPENING state if it is already in an open state or
-            // it has been allowed to by tryOpen().
+            // it has been allowed to by tryOpenCamera().
             Preconditions.checkState(isOpen(state) || previousState == CameraInternal.State.OPENING,
                     "Cannot mark camera as opening until camera was successful at calling "
-                            + "CameraAvailabilityRegistry.tryOpen()");
+                            + "CameraStateRegistry.tryOpenCamera()");
         }
 
         // Only update the available camera count if the camera state has changed.
@@ -235,9 +235,9 @@
             mDebugString.append(
                     "-------------------------------------------------------------------\n");
         }
-        // Count the number of cameras that are not in a closed state state. Closed states are
+        // Count the number of cameras that are not in a closed state. Closed states are
         // considered to be CLOSED, PENDING_OPEN or OPENING, since we can't guarantee a camera
-        // has actually be open in these states. All cameras that are in a CLOSING or RELEASING
+        // has actually been open in these states. All cameras that are in a CLOSING or RELEASING
         // state may have previously been open, so we will count them as open.
         int openCount = 0;
         for (Map.Entry<Camera, CameraRegistration> entry : mCameraStates.entrySet()) {
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 4d1fad26..7aab12a 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -231,7 +231,6 @@
     field public static final androidx.car.app.model.CarIcon BACK;
     field public static final androidx.car.app.model.CarIcon ERROR;
     field public static final int TYPE_ALERT = 4; // 0x4
-    field @Deprecated public static final int TYPE_APP = 5; // 0x5
     field public static final int TYPE_APP_ICON = 5; // 0x5
     field public static final int TYPE_BACK = 3; // 0x3
     field public static final int TYPE_CUSTOM = 1; // 0x1
@@ -357,7 +356,6 @@
   }
 
   public final class ItemList {
-    method @Deprecated public java.util.List<androidx.car.app.model.Item!> getItemList();
     method public java.util.List<androidx.car.app.model.Item!> getItems();
     method public androidx.car.app.model.CarText? getNoItemsMessage();
     method public androidx.car.app.model.OnItemVisibilityChangedDelegate? getOnItemVisibilityChangedDelegate();
@@ -386,7 +384,6 @@
   public final class ListTemplate implements androidx.car.app.model.Template {
     method public androidx.car.app.model.ActionStrip? getActionStrip();
     method public androidx.car.app.model.Action? getHeaderAction();
-    method @Deprecated public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionLists();
     method public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionedLists();
     method public androidx.car.app.model.ItemList? getSingleList();
     method public androidx.car.app.model.CarText? getTitle();
@@ -395,7 +392,6 @@
 
   public static final class ListTemplate.Builder {
     ctor public ListTemplate.Builder();
-    method @Deprecated public androidx.car.app.model.ListTemplate.Builder addList(androidx.car.app.model.ItemList, CharSequence);
     method public androidx.car.app.model.ListTemplate.Builder addSectionedList(androidx.car.app.model.SectionedItemList);
     method public androidx.car.app.model.ListTemplate build();
     method public androidx.car.app.model.ListTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
@@ -460,7 +456,6 @@
 
   public final class Pane {
     method public java.util.List<androidx.car.app.model.Action!> getActions();
-    method @Deprecated public java.util.List<androidx.car.app.model.Row!> getRowList();
     method public java.util.List<androidx.car.app.model.Row!> getRows();
     method public boolean isLoading();
   }
@@ -607,7 +602,6 @@
   }
 
   public final class SectionedItemList {
-    method @Deprecated public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, androidx.car.app.model.CarText);
     method public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, CharSequence);
     method public androidx.car.app.model.CarText? getHeader();
     method public androidx.car.app.model.ItemList? getItemList();
@@ -618,7 +612,6 @@
 
   public final class TemplateInfo {
     ctor public TemplateInfo(Class<? extends androidx.car.app.model.Template>, String);
-    ctor @Deprecated public TemplateInfo(androidx.car.app.model.Template, String);
     method public Class<? extends androidx.car.app.model.Template> getTemplateClass();
     method public String getTemplateId();
   }
@@ -751,14 +744,12 @@
     field public static final int TYPE_ON_RAMP_SLIGHT_RIGHT = 14; // 0xe
     field public static final int TYPE_ON_RAMP_U_TURN_LEFT = 19; // 0x13
     field public static final int TYPE_ON_RAMP_U_TURN_RIGHT = 20; // 0x14
-    field @Deprecated public static final int TYPE_ROUNDABOUT_ENTER = 30; // 0x1e
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW = 34; // 0x22
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE = 35; // 0x23
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW = 32; // 0x20
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE = 33; // 0x21
     field public static final int TYPE_ROUNDABOUT_ENTER_CCW = 45; // 0x2d
     field public static final int TYPE_ROUNDABOUT_ENTER_CW = 43; // 0x2b
-    field @Deprecated public static final int TYPE_ROUNDABOUT_EXIT = 31; // 0x1f
     field public static final int TYPE_ROUNDABOUT_EXIT_CCW = 46; // 0x2e
     field public static final int TYPE_ROUNDABOUT_EXIT_CW = 44; // 0x2c
     field public static final int TYPE_STRAIGHT = 36; // 0x24
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 4d1fad26..7aab12a 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -231,7 +231,6 @@
     field public static final androidx.car.app.model.CarIcon BACK;
     field public static final androidx.car.app.model.CarIcon ERROR;
     field public static final int TYPE_ALERT = 4; // 0x4
-    field @Deprecated public static final int TYPE_APP = 5; // 0x5
     field public static final int TYPE_APP_ICON = 5; // 0x5
     field public static final int TYPE_BACK = 3; // 0x3
     field public static final int TYPE_CUSTOM = 1; // 0x1
@@ -357,7 +356,6 @@
   }
 
   public final class ItemList {
-    method @Deprecated public java.util.List<androidx.car.app.model.Item!> getItemList();
     method public java.util.List<androidx.car.app.model.Item!> getItems();
     method public androidx.car.app.model.CarText? getNoItemsMessage();
     method public androidx.car.app.model.OnItemVisibilityChangedDelegate? getOnItemVisibilityChangedDelegate();
@@ -386,7 +384,6 @@
   public final class ListTemplate implements androidx.car.app.model.Template {
     method public androidx.car.app.model.ActionStrip? getActionStrip();
     method public androidx.car.app.model.Action? getHeaderAction();
-    method @Deprecated public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionLists();
     method public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionedLists();
     method public androidx.car.app.model.ItemList? getSingleList();
     method public androidx.car.app.model.CarText? getTitle();
@@ -395,7 +392,6 @@
 
   public static final class ListTemplate.Builder {
     ctor public ListTemplate.Builder();
-    method @Deprecated public androidx.car.app.model.ListTemplate.Builder addList(androidx.car.app.model.ItemList, CharSequence);
     method public androidx.car.app.model.ListTemplate.Builder addSectionedList(androidx.car.app.model.SectionedItemList);
     method public androidx.car.app.model.ListTemplate build();
     method public androidx.car.app.model.ListTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
@@ -460,7 +456,6 @@
 
   public final class Pane {
     method public java.util.List<androidx.car.app.model.Action!> getActions();
-    method @Deprecated public java.util.List<androidx.car.app.model.Row!> getRowList();
     method public java.util.List<androidx.car.app.model.Row!> getRows();
     method public boolean isLoading();
   }
@@ -607,7 +602,6 @@
   }
 
   public final class SectionedItemList {
-    method @Deprecated public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, androidx.car.app.model.CarText);
     method public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, CharSequence);
     method public androidx.car.app.model.CarText? getHeader();
     method public androidx.car.app.model.ItemList? getItemList();
@@ -618,7 +612,6 @@
 
   public final class TemplateInfo {
     ctor public TemplateInfo(Class<? extends androidx.car.app.model.Template>, String);
-    ctor @Deprecated public TemplateInfo(androidx.car.app.model.Template, String);
     method public Class<? extends androidx.car.app.model.Template> getTemplateClass();
     method public String getTemplateId();
   }
@@ -751,14 +744,12 @@
     field public static final int TYPE_ON_RAMP_SLIGHT_RIGHT = 14; // 0xe
     field public static final int TYPE_ON_RAMP_U_TURN_LEFT = 19; // 0x13
     field public static final int TYPE_ON_RAMP_U_TURN_RIGHT = 20; // 0x14
-    field @Deprecated public static final int TYPE_ROUNDABOUT_ENTER = 30; // 0x1e
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW = 34; // 0x22
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE = 35; // 0x23
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW = 32; // 0x20
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE = 33; // 0x21
     field public static final int TYPE_ROUNDABOUT_ENTER_CCW = 45; // 0x2d
     field public static final int TYPE_ROUNDABOUT_ENTER_CW = 43; // 0x2b
-    field @Deprecated public static final int TYPE_ROUNDABOUT_EXIT = 31; // 0x1f
     field public static final int TYPE_ROUNDABOUT_EXIT_CCW = 46; // 0x2e
     field public static final int TYPE_ROUNDABOUT_EXIT_CW = 44; // 0x2c
     field public static final int TYPE_STRAIGHT = 36; // 0x24
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 4d1fad26..7aab12a 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -231,7 +231,6 @@
     field public static final androidx.car.app.model.CarIcon BACK;
     field public static final androidx.car.app.model.CarIcon ERROR;
     field public static final int TYPE_ALERT = 4; // 0x4
-    field @Deprecated public static final int TYPE_APP = 5; // 0x5
     field public static final int TYPE_APP_ICON = 5; // 0x5
     field public static final int TYPE_BACK = 3; // 0x3
     field public static final int TYPE_CUSTOM = 1; // 0x1
@@ -357,7 +356,6 @@
   }
 
   public final class ItemList {
-    method @Deprecated public java.util.List<androidx.car.app.model.Item!> getItemList();
     method public java.util.List<androidx.car.app.model.Item!> getItems();
     method public androidx.car.app.model.CarText? getNoItemsMessage();
     method public androidx.car.app.model.OnItemVisibilityChangedDelegate? getOnItemVisibilityChangedDelegate();
@@ -386,7 +384,6 @@
   public final class ListTemplate implements androidx.car.app.model.Template {
     method public androidx.car.app.model.ActionStrip? getActionStrip();
     method public androidx.car.app.model.Action? getHeaderAction();
-    method @Deprecated public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionLists();
     method public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionedLists();
     method public androidx.car.app.model.ItemList? getSingleList();
     method public androidx.car.app.model.CarText? getTitle();
@@ -395,7 +392,6 @@
 
   public static final class ListTemplate.Builder {
     ctor public ListTemplate.Builder();
-    method @Deprecated public androidx.car.app.model.ListTemplate.Builder addList(androidx.car.app.model.ItemList, CharSequence);
     method public androidx.car.app.model.ListTemplate.Builder addSectionedList(androidx.car.app.model.SectionedItemList);
     method public androidx.car.app.model.ListTemplate build();
     method public androidx.car.app.model.ListTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
@@ -460,7 +456,6 @@
 
   public final class Pane {
     method public java.util.List<androidx.car.app.model.Action!> getActions();
-    method @Deprecated public java.util.List<androidx.car.app.model.Row!> getRowList();
     method public java.util.List<androidx.car.app.model.Row!> getRows();
     method public boolean isLoading();
   }
@@ -607,7 +602,6 @@
   }
 
   public final class SectionedItemList {
-    method @Deprecated public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, androidx.car.app.model.CarText);
     method public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, CharSequence);
     method public androidx.car.app.model.CarText? getHeader();
     method public androidx.car.app.model.ItemList? getItemList();
@@ -618,7 +612,6 @@
 
   public final class TemplateInfo {
     ctor public TemplateInfo(Class<? extends androidx.car.app.model.Template>, String);
-    ctor @Deprecated public TemplateInfo(androidx.car.app.model.Template, String);
     method public Class<? extends androidx.car.app.model.Template> getTemplateClass();
     method public String getTemplateId();
   }
@@ -751,14 +744,12 @@
     field public static final int TYPE_ON_RAMP_SLIGHT_RIGHT = 14; // 0xe
     field public static final int TYPE_ON_RAMP_U_TURN_LEFT = 19; // 0x13
     field public static final int TYPE_ON_RAMP_U_TURN_RIGHT = 20; // 0x14
-    field @Deprecated public static final int TYPE_ROUNDABOUT_ENTER = 30; // 0x1e
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW = 34; // 0x22
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE = 35; // 0x23
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW = 32; // 0x20
     field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE = 33; // 0x21
     field public static final int TYPE_ROUNDABOUT_ENTER_CCW = 45; // 0x2d
     field public static final int TYPE_ROUNDABOUT_ENTER_CW = 43; // 0x2b
-    field @Deprecated public static final int TYPE_ROUNDABOUT_EXIT = 31; // 0x1f
     field public static final int TYPE_ROUNDABOUT_EXIT_CCW = 46; // 0x2e
     field public static final int TYPE_ROUNDABOUT_EXIT_CW = 44; // 0x2c
     field public static final int TYPE_STRAIGHT = 36; // 0x24
diff --git a/car/app/app/src/main/java/androidx/car/app/AppManager.java b/car/app/app/src/main/java/androidx/car/app/AppManager.java
index 169b87a..bc7163e 100644
--- a/car/app/app/src/main/java/androidx/car/app/AppManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/AppManager.java
@@ -88,7 +88,6 @@
      *
      * @param text     the text to show
      * @param duration how long to display the message
-     *
      * @throws HostException if the remote call fails
      */
     public void showToast(@NonNull CharSequence text, int duration) {
diff --git a/car/app/app/src/main/java/androidx/car/app/CarContext.java b/car/app/app/src/main/java/androidx/car/app/CarContext.java
index ec1e87a..2596a6b 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarContext.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarContext.java
@@ -157,9 +157,7 @@
      * @param name The name of the car service requested. This should be one of
      *             {@link #APP_SERVICE},
      *             {@link #NAVIGATION_SERVICE} or {@link #SCREEN_SERVICE}.
-     *
      * @return The car service instance
-     *
      * @throws IllegalArgumentException if {@code name} does not refer to a valid car service
      * @throws NullPointerException     if {@code name} is {@code null}
      */
@@ -187,9 +185,7 @@
      * ScreenManager}.
      *
      * @param serviceClass the class of the requested service
-     *
      * @return The car service instance
-     *
      * @throws IllegalArgumentException if {@code serviceClass} is not the class of a supported car
      *                                  service
      * @throws NullPointerException     if {@code serviceClass} is {@code null}
@@ -203,13 +199,10 @@
      * Gets the name of the car service that is represented by the specified class.
      *
      * @param serviceClass the class of the requested service
-     *
      * @return the car service name to use with {@link #getCarService(String)}
-     *
      * @throws IllegalArgumentException if {@code serviceClass} is not the class of a supported car
      *                                  service
      * @throws NullPointerException     if {@code serviceClass} is {@code null}
-     *
      * @see #getCarService
      */
     @NonNull
@@ -254,7 +247,6 @@
      * </dl>
      *
      * @param intent the {@link Intent} to send to the target application
-     *
      * @throws SecurityException         if the app attempts to start a different app explicitly or
      *                                   does not have permissions for the requested action
      * @throws InvalidParameterException if {@code intent} does not meet the criteria defined
@@ -282,7 +274,6 @@
      * @param appIntent          the {@link Intent} to use for starting the car app. See {@link
      *                           #startCarApp(Intent)} for the documentation on valid
      *                           {@link Intent}s
-     *
      * @throws InvalidParameterException if {@code notificationIntent} is not an {@link Intent}
      *                                   received from a broadcast, due to an action taken by the
      *                                   user in the car
@@ -477,7 +468,6 @@
      * @return a value between {@link AppInfo#getMinCarAppApiLevel()} and
      * {@link AppInfo#getLatestCarAppApiLevel()}. In case of incompatibility, the host will
      * disconnect from the service before completing the handshake
-     *
      * @throws IllegalStateException if invoked before the connection handshake with the host has
      *                               been completed (for example, before
      *                               {@link Session#onCreateScreen(Intent)})
diff --git a/car/app/app/src/main/java/androidx/car/app/CarToast.java b/car/app/app/src/main/java/androidx/car/app/CarToast.java
index c852365..645a212 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarToast.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarToast.java
@@ -76,7 +76,6 @@
      *                  text will be set to empty
      * @param duration  how long to display the message. Either {@link #LENGTH_SHORT} or {@link
      *                  #LENGTH_LONG}
-     *
      * @throws NullPointerException if {@code carContext} is {@code null}
      */
     @NonNull
@@ -94,7 +93,6 @@
      * @param text     the text to show
      * @param duration how long to display the message. Either {@link #LENGTH_SHORT} or {@link
      *                 #LENGTH_LONG}
-     *
      * @throws NullPointerException if either the {@code carContext} or the {@code text} are {@code
      *                              null}
      */
diff --git a/car/app/app/src/main/java/androidx/car/app/HostDispatcher.java b/car/app/app/src/main/java/androidx/car/app/HostDispatcher.java
index 890b0c8..98fb4a0 100644
--- a/car/app/app/src/main/java/androidx/car/app/HostDispatcher.java
+++ b/car/app/app/src/main/java/androidx/car/app/HostDispatcher.java
@@ -55,7 +55,6 @@
      * @param hostType the service to dispatch to
      * @param call     the request to dispatch
      * @param callName the name of the call for logging purposes
-     *
      * @throws SecurityException if the host has thrown it
      * @throws HostException     if the host throws any exception other than
      *                           {@link SecurityException}
@@ -63,7 +62,6 @@
     @Nullable
     @SuppressWarnings({"unchecked", "cast.unsafe"}) // Cannot check if instanceof ServiceT
     @SuppressLint("LambdaLast")
-    // TODO(rampara): Change method signature to change parameter order.
     public <ServiceT, ReturnT> ReturnT dispatch(
             @CarServiceType @NonNull String hostType, @NonNull HostCall<ServiceT, ReturnT> call,
             @NonNull String callName) {
diff --git a/car/app/app/src/main/java/androidx/car/app/Screen.java b/car/app/app/src/main/java/androidx/car/app/Screen.java
index ff3318f..9dfa10e 100644
--- a/car/app/app/src/main/java/androidx/car/app/Screen.java
+++ b/car/app/app/src/main/java/androidx/car/app/Screen.java
@@ -217,8 +217,6 @@
         return mCarContext.getCarService(ScreenManager.class);
     }
 
-    // TODO(rampara): Replace code tags with link on submission of notification module
-
     /**
      * Returns the {@link Template} to present in the car screen.
      *
@@ -296,7 +294,7 @@
      * an app to begin a new task flow from notifications, and it holds true even if an app is
      * already bound and in the foreground.
      *
-     * <p>See {@code androidx.car.app.notification.CarAppExtender} for details on notifications.
+     * <p>See {@link androidx.car.app.notification.CarAppExtender} for details on notifications.
      */
     @NonNull
     public abstract Template onGetTemplate();
diff --git a/car/app/app/src/main/java/androidx/car/app/ScreenManager.java b/car/app/app/src/main/java/androidx/car/app/ScreenManager.java
index 2ccb296..35f7cfe 100644
--- a/car/app/app/src/main/java/androidx/car/app/ScreenManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/ScreenManager.java
@@ -91,7 +91,6 @@
      * @param onScreenResultListener the listener that will be executed with the result pushed by
      *                               the {@code screen} through {@link Screen#setResult}. This
      *                               callback will be executed on the main thread
-     *
      * @throws NullPointerException  if either the {@code screen} or the {@code
      *                               onScreenResultCallback} are {@code null}
      * @throws IllegalStateException if the current thread is not the main thread
@@ -126,7 +125,6 @@
      *
      * @throws NullPointerException  if {@code marker} is {@code null}
      * @throws IllegalStateException if the current thread is not the main thread
-     *
      * @see Screen#setMarker
      */
     public void popTo(@NonNull String marker) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Action.java b/car/app/app/src/main/java/androidx/car/app/model/Action.java
index 4bcb33f..84dd4f6 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Action.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Action.java
@@ -65,7 +65,6 @@
      *
      * @hide
      */
-    // TODO(shiufai): investigate how to expose IntDefs if needed.
     @RestrictTo(LIBRARY)
     @IntDef(
             value = {
@@ -135,8 +134,10 @@
     private final int mType;
 
     /**
-     * Returns the title displayed in the action, or {@code null} if the action does not have a
+     * Returns the title displayed in the action or {@code null} if the action does not have a
      * title.
+     *
+     * @see Builder#setTitle(CharSequence)
      */
     @Nullable
     public CarText getTitle() {
@@ -144,8 +145,10 @@
     }
 
     /**
-     * Returns the {@link CarIcon} to displayed in the action, or {@code null} if the action does
+     * Returns the {@link CarIcon} to display in the action or {@code null} if the action does
      * not have an icon.
+     *
+     * @see Builder#setIcon(CarIcon)
      */
     @Nullable
     public CarIcon getIcon() {
@@ -154,6 +157,8 @@
 
     /**
      * Returns the {@link CarColor} used for the background color of the action.
+     *
+     * @see Builder#setBackgroundColor(CarColor)
      */
     @Nullable
     public CarColor getBackgroundColor() {
@@ -339,7 +344,6 @@
          *
          * @param backgroundColor the {@link CarColor} to set as background. Use {@link
          *                        CarColor#DEFAULT} to let the host pick a default
-         *
          * @throws IllegalArgumentException if {@code backgroundColor} is not a standard color
          * @throws NullPointerException     if {@code backgroundColor} is {@code null}
          */
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java b/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java
index 5f853ae..b28bc58 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java
@@ -46,7 +46,9 @@
     private final List<Action> mActions;
 
     /**
-     * Returns the list of {@link Action}'s.
+     * Returns the list of {@link Action}s in the strip.
+     *
+     * @see Builder#addAction(Action)
      */
     @NonNull
     public List<Action> getActions() {
@@ -54,7 +56,7 @@
     }
 
     /**
-     * Returns the first {@link Action} associated with the input {@code actionType}, or {@code
+     * Returns the first {@link Action} associated with the input {@code actionType} or {@code
      * null} if no matching {@link Action} is found.
      */
     @Nullable
@@ -114,8 +116,8 @@
          *
          * @throws IllegalArgumentException if the background color of the action is specified,
          *                                  or if {@code action} is a standard action and an
-         *                                  action of the same type has already been added.
-         * @throws NullPointerException     if {@code action} is {@code null}.
+         *                                  action of the same type has already been added
+         * @throws NullPointerException     if {@code action} is {@code null}
          */
         @NonNull
         public Builder addAction(@NonNull Action action) {
@@ -137,7 +139,7 @@
         /**
          * Constructs the {@link ActionStrip} defined by this builder.
          *
-         * @throws IllegalStateException if the action strip is empty.
+         * @throws IllegalStateException if the action strip is empty
          */
         @NonNull
         public ActionStrip build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarColor.java b/car/app/app/src/main/java/androidx/car/app/model/CarColor.java
index b452602..06495ea 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarColor.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarColor.java
@@ -29,8 +29,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
-// TODO(shiufai): Add link to color guidelines.
-
 /**
  * Represents a color to be used in a car app.
  *
@@ -93,7 +91,6 @@
      *
      * @hide
      */
-    // TODO(shiufai): investigate how to expose IntDefs if needed.
     @IntDef(
             value = {
                     TYPE_CUSTOM,
@@ -176,15 +173,14 @@
      * Indicates that a default color should be used.
      *
      * <p>This can be used for example to tell the host that the app has no preference for the
-     * tint of
-     * an icon, and it should use whatever default it finds appropriate.
+     * tint of an icon, and it should use whatever default it finds appropriate.
      */
     @NonNull
     public static final CarColor DEFAULT = create(TYPE_DEFAULT);
 
     /**
      * Indicates that the app primary color and its dark version should be used, as declared in the
-     * app manifest through the <code>carColorPrimary</code> and <code>carColorPrimaryDark</code>
+     * app manifest through the {@code carColorPrimary} and {@code carColorPrimaryDark}
      * theme attributes.
      */
     @NonNull
@@ -192,10 +188,8 @@
 
     /**
      * Indicates that the app secondary color and its dark version should be used, as declared in
-     * the
-     * app manifest through the <code>carColorSecondary</code> and
-     * <code>carColorSecondaryDark</code>
-     * theme attributes.
+     * the app manifest through the <code>carColorSecondary</code> and {@code
+     * carColorSecondaryDark} theme attributes.
      */
     @NonNull
     public static final CarColor SECONDARY = create(TYPE_SECONDARY);
@@ -241,16 +235,25 @@
         return new CarColor(TYPE_CUSTOM, color, colorDark);
     }
 
+    /** Returns the type of color for this instance. */
     @CarColorType
     public int getType() {
         return mType;
     }
 
+    /**
+     * Returns a packed color int for the light variant of the color, used when the type
+     * is {@link #TYPE_CUSTOM}.
+     */
     @ColorInt
     public int getColor() {
         return mColor;
     }
 
+    /**
+     * Returns a packed color int for the dark variant of the color, used when the type
+     * is {@link #TYPE_CUSTOM}.
+     */
     @ColorInt
     public int getColorDark() {
         return mColorDark;
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
index 3df13af..6741860 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
@@ -104,7 +104,6 @@
      *
      * @hide
      */
-    // TODO(shiufai): investigate how to expose IntDefs if needed.
     @RestrictTo(LIBRARY)
     @SuppressLint("UniqueConstants") // TYPE_APP will be removed in a follow-up change.
     @IntDef(
@@ -112,7 +111,6 @@
                     TYPE_CUSTOM,
                     TYPE_BACK,
                     TYPE_ALERT,
-                    TYPE_APP,
                     TYPE_APP_ICON,
                     TYPE_ERROR,
             })
@@ -143,16 +141,6 @@
      * The app's icon.
      *
      * @see #APP_ICON
-     * @deprecated use {@link #TYPE_APP_ICON} instead.
-     */
-    // TODO(b/176937077): remove after the host references TYPE_APP_ICON.
-    @Deprecated
-    public static final int TYPE_APP = 5;
-
-    /**
-     * The app's icon.
-     *
-     * @see #APP_ICON
      */
     public static final int TYPE_APP_ICON = 5;
 
@@ -170,12 +158,21 @@
     @NonNull
     public static final CarIcon APP_ICON = CarIcon.forStandardType(TYPE_APP_ICON);
 
+    /**
+     * An icon representing a "back" action.
+     */
     @NonNull
     public static final CarIcon BACK = CarIcon.forStandardType(TYPE_BACK);
 
+    /**
+     * An icon representing an alert.
+     */
     @NonNull
     public static final CarIcon ALERT = CarIcon.forStandardType(TYPE_ALERT);
 
+    /**
+     * An icon representing an error.
+     */
     @NonNull
     public static final CarIcon ERROR = CarIcon.forStandardType(TYPE_ERROR);
 
@@ -189,16 +186,30 @@
     @Nullable
     private final CarColor mTint;
 
+    /**
+     * Returns the {@link IconCompat} instance backing by this car icon or {@code null} if one
+     * isn't set.
+     *
+     * @see Builder#Builder(IconCompat)
+     */
     @Nullable
     public IconCompat getIcon() {
         return mIcon;
     }
 
+    /**
+     * Returns the tint of the icon or {@code null} if not set.
+     *
+     * @see Builder#setTint(CarColor)
+     */
     @Nullable
     public CarColor getTint() {
         return mTint;
     }
 
+    /**
+     * Returns the type of car icon for this instance.
+     */
     @CarIconType
     public int getType() {
         return mType;
@@ -260,7 +271,7 @@
             return false;
         }
 
-        // TODO(shiufai): Decide how/if we will diff bitmap type IconCompat
+        // TODO(b/146175636): Decide how/if we will diff bitmap type IconCompat
         if (type == TYPE_RESOURCE) {
             return Objects.equals(mIcon.getResPackage(), other.getResPackage())
                     && mIcon.getResId() == other.getResId();
@@ -325,7 +336,6 @@
         /**
          * Sets the tint of the icon to the given {@link CarColor}.
          *
-         *
          * <p>This tint overrides the tint set through {@link IconCompat#setTint(int)} in the
          * backing {@link IconCompat} with a {@link CarColor} tint.The tint set through {@link
          * IconCompat#setTint(int)} is not guaranteed to be applied if the {@link CarIcon} tint
@@ -335,8 +345,7 @@
          *
          * <p>By default, no tint is set unless one is specified with this method.
          *
-         * @throws NullPointerException if {@code tin} is {@code null}.
-         *
+         * @throws NullPointerException if {@code tin} is {@code null}
          * @see CarColor
          * @see android.graphics.drawable.Drawable#setTintMode(Mode)
          */
@@ -364,9 +373,8 @@
          *   <li>{@link IconCompat#TYPE_URI}
          * </ul>
          *
-         * <p>{@link IconCompat#TYPE_URI} is only supported in templates that explicitly allow it
-         * . In
-         * those cases, the appropriate APIs will be documented to indicate this.
+         * <p>{@link IconCompat#TYPE_URI} is only supported in templates that explicitly allow it.
+         * In those cases, the appropriate APIs will be documented to indicate this.
          *
          * <p>For {@link IconCompat#TYPE_URI}, the URI's scheme must be {@link
          * ContentResolver#SCHEME_CONTENT}.
@@ -374,8 +382,8 @@
          * <p>If the icon image is loaded from URI, it may be cached on the host side. Changing the
          * contents of the URI will result in the host showing a stale image.
          *
-         * @throws IllegalArgumentException if {@code icon}'s URI scheme is not supported.
-         * @throws NullPointerException     if {@code icon} is {@code null}.
+         * @throws IllegalArgumentException if {@code icon}'s URI scheme is not supported
+         * @throws NullPointerException     if {@code icon} is {@code null}
          */
         public Builder(@NonNull IconCompat icon) {
             CarIconConstraints.UNCONSTRAINED.checkSupportedIcon(requireNonNull(icon));
@@ -388,7 +396,7 @@
          * Returns a {@link Builder} instance configured with the same data as the given
          * {@link CarIcon} instance.
          *
-         * @throws NullPointerException if {@code icon} is {@code null}.
+         * @throws NullPointerException if {@code icon} is {@code null}
          */
         public Builder(@NonNull CarIcon carIcon) {
             requireNonNull(carIcon);
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarIconSpan.java b/car/app/app/src/main/java/androidx/car/app/model/CarIconSpan.java
index 1c91d363..ad83c97 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarIconSpan.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarIconSpan.java
@@ -71,7 +71,6 @@
                     ALIGN_BASELINE,
             })
     @Retention(RetentionPolicy.SOURCE)
-    // TODO(shiufai): investigate how to expose IntDefs if needed.
     @RestrictTo(LIBRARY)
     public @interface Alignment {
     }
@@ -108,7 +107,7 @@
      * Creates a {@link CarIconSpan} from a {@link CarIcon} with a default alignment of {@link
      * #ALIGN_BASELINE}.
      *
-     * @throws NullPointerException if {@code icon} is {@code null}.
+     * @throws NullPointerException if {@code icon} is {@code null}
      * @see #create(CarIcon, int)
      */
     @NonNull
@@ -118,15 +117,16 @@
 
     /**
      * Creates a {@link CarIconSpan} from a {@link CarIcon}, specifying the alignment of the icon
-     * with
-     * respect to its surrounding text.
+     * with respect to its surrounding text.
      *
-     * @param icon      the {@link CarIcon} to replace the text with.
+     * @param icon      the {@link CarIcon} to replace the text with
      * @param alignment the alignment of the {@link CarIcon} relative to the text. This should be
      *                  one of {@link #ALIGN_BASELINE}, {@link #ALIGN_BOTTOM} or
-     *                  {@link #ALIGN_CENTER}.
-     * @throws NullPointerException     if {@code icon} is {@code null}.
-     * @throws IllegalArgumentException if {@code alignment} is not a valid value.
+     *                  {@link #ALIGN_CENTER}
+     *
+     * @throws NullPointerException     if {@code icon} is {@code null}
+     * @throws IllegalArgumentException if {@code alignment} is not a valid value
+     *
      * @see #ALIGN_BASELINE
      * @see #ALIGN_BOTTOM
      * @see #ALIGN_CENTER
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarLocation.java b/car/app/app/src/main/java/androidx/car/app/model/CarLocation.java
index 674dd18..53a7200 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarLocation.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarLocation.java
@@ -42,7 +42,7 @@
      * Returns a new instance of a {@link CarLocation} with the same latitude and longitude
      * contained in the given {@link Location}.
      *
-     * @throws NullPointerException if {@code location} is {@code null}.
+     * @throws NullPointerException if {@code location} is {@code null}
      */
     @NonNull
     public static CarLocation create(@NonNull Location location) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarText.java b/car/app/app/src/main/java/androidx/car/app/model/CarText.java
index 7762286..22e5c1d 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarText.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarText.java
@@ -58,6 +58,7 @@
         return new CarText(text);
     }
 
+    /** Returns whether the text string is empty. */
     public boolean isEmpty() {
         return mText.isEmpty();
     }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java b/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java
index b62f7b9..11bb852 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java
@@ -34,6 +34,7 @@
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 import java.time.format.TextStyle;
+import java.util.Date;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.TimeZone;
@@ -85,18 +86,11 @@
 
     @Override
     @NonNull
-    @RequiresApi(26)
-    // TODO(shiufai): consider removing the @RequiresApi annotation for a toString method.
-    @SuppressLint("UnsafeNewApiCall")
     public String toString() {
-        return "[local: "
-                + LocalDateTime.ofEpochSecond(
-                mTimeSinceEpochMillis / 1000,
-                /* nanoOfSecond= */ 0,
-                ZoneOffset.ofTotalSeconds(mZoneOffsetSeconds))
-                + ", zone: "
-                + mZoneShortName
-                + "]";
+        return "[time since epoch (ms): " + mTimeSinceEpochMillis
+                + "( " + new Date(mTimeSinceEpochMillis) + ") "
+                + " zone offset (s): " + mZoneOffsetSeconds
+                + ", zone: " + mZoneShortName + "]";
     }
 
     @Override
@@ -123,19 +117,19 @@
      * Returns an instance of a {@link DateTimeWithZone}.
      *
      * @param timeSinceEpochMillis The number of milliseconds from the epoch of
-     *                             1970-01-01T00:00:00Z.
+     *                             1970-01-01T00:00:00Z
      * @param zoneOffsetSeconds    The offset of the time zone from UTC at the date specified by
      *                             {@code timeInUtcMillis}. This offset must be in the range
      *                             {@code -18:00} to {@code +18:00}, which corresponds to -64800
-     *                             to +64800.
+     *                             to +64800
      * @param zoneShortName        The abbreviated name of the time zone, for example, "PST" for
      *                             Pacific Standard Time. This string may be used to display to
      *                             the user along with the date when needed, for example, if this
-     *                             time zone is different than the current system time zone.
+     *                             time zone is different than the current system time zone
      * @throws IllegalArgumentException if {@code timeSinceEpochMillis} is a negative value, if
      *                                  {@code zoneOffsetSeconds} is not within the required range,
-     *                                  or if {@code zoneShortName} is empty.
-     * @throws NullPointerException     if {@code zoneShortName} is {@code null}.
+     *                                  or if {@code zoneShortName} is empty
+     * @throws NullPointerException     if {@code zoneShortName} is {@code null}
      */
     @NonNull
     public static DateTimeWithZone create(
@@ -158,14 +152,14 @@
      * Returns an instance of a {@link DateTimeWithZone}.
      *
      * @param timeSinceEpochMillis The number of milliseconds from the epoch of
-     *                             1970-01-01T00:00:00Z.
+     *                             1970-01-01T00:00:00Z
      * @param timeZone             The time zone at the date specified by {@code timeInUtcMillis}.
      *                             The abbreviated name of this time zone, formatted using the
      *                             default locale, may be displayed to the user when needed, for
      *                             example, if this time zone is different than the current
-     *                             system time zone.
-     * @throws IllegalArgumentException if {@code timeSinceEpochMillis} is a negative value.
-     * @throws NullPointerException     if {@code timeZone} is {@code null}.
+     *                             system time zone
+     * @throws IllegalArgumentException if {@code timeSinceEpochMillis} is a negative value
+     * @throws NullPointerException     if {@code timeZone} is {@code null}
      */
     @NonNull
     public static DateTimeWithZone create(long timeSinceEpochMillis, @NonNull TimeZone timeZone) {
@@ -186,8 +180,8 @@
      * @param zonedDateTime The time with a time zone. The abbreviated name of this time zone,
      *                      formatted using the default locale, may be displayed to the user when
      *                      needed, for example, if this time zone is different than the current
-     *                      system time zone.
-     * @throws NullPointerException if {@code zonedDateTime} is {@code null}.
+     *                      system time zone
+     * @throws NullPointerException if {@code zonedDateTime} is {@code null}
      */
     // TODO(shiufai): revisit wrapping this method in a container class (e.g. Api26Impl).
     @SuppressLint("UnsafeNewApiCall")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Distance.java b/car/app/app/src/main/java/androidx/car/app/model/Distance.java
index 2f1ea2d..7df2b6b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Distance.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Distance.java
@@ -46,7 +46,6 @@
             UNIT_MILES_P1
     })
     @Retention(RetentionPolicy.SOURCE)
-    // TODO(shiufai): investigate how to expose IntDefs if needed.
     @RestrictTo(LIBRARY)
     public @interface Unit {
     }
@@ -114,11 +113,12 @@
      * "1.5 km", "1.0 km", and so on.
      *
      * @param displayDistance the distance to display, in the units specified in {@code
-     *                        displayUnit}. See {@link #getDisplayDistance()}.
+     *                        displayUnit}. See {@link #getDisplayDistance()}
      * @param displayUnit     the unit of distance to use when displaying the value in {@code
      *                        displayUnit}. This should be one of the {@code UNIT_*} static
-     *                        constants defined in this class. See {@link #getDisplayUnit()}.
-     * @throws IllegalArgumentException if {@code displayDistance} is negative.
+     *                        constants defined in this class. See {@link #getDisplayUnit()}
+     *
+     * @throws IllegalArgumentException if {@code displayDistance} is negative
      */
     @NonNull
     public static Distance create(double displayDistance, @Unit int displayUnit) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/DistanceSpan.java b/car/app/app/src/main/java/androidx/car/app/model/DistanceSpan.java
index a931389..1d01ee08 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/DistanceSpan.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/DistanceSpan.java
@@ -68,14 +68,10 @@
         return new DistanceSpan(requireNonNull(distance));
     }
 
-    private DistanceSpan(Distance distance) {
-        this.mDistance = distance;
-    }
-
-    private DistanceSpan() {
-        mDistance = null;
-    }
-
+    /**
+     * Returns the {@link Distance} instance associated with this span or {@code null} if not
+     * set.
+     */
     @Nullable
     public Distance getDistance() {
         return mDistance;
@@ -104,4 +100,12 @@
 
         return Objects.equals(mDistance, otherSpan.mDistance);
     }
+
+    private DistanceSpan(Distance distance) {
+        this.mDistance = distance;
+    }
+
+    private DistanceSpan() {
+        mDistance = null;
+    }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java b/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java
index 83000b7..a015208 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java
@@ -71,14 +71,7 @@
         return new DurationSpan(requireNonNull(duration).getSeconds());
     }
 
-    private DurationSpan(long durationSeconds) {
-        this.mDurationSeconds = durationSeconds;
-    }
-
-    private DurationSpan() {
-        mDurationSeconds = 0;
-    }
-
+    /** Returns the time duration associated with this span, in seconds. */
     @SuppressLint("MethodNameUnits")
     public long getDurationSeconds() {
         return mDurationSeconds;
@@ -108,4 +101,12 @@
 
         return mDurationSeconds == otherSpan.mDurationSeconds;
     }
+
+    private DurationSpan(long durationSeconds) {
+        this.mDurationSeconds = durationSeconds;
+    }
+
+    private DurationSpan() {
+        mDurationSeconds = 0;
+    }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java b/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java
index ca11229..4edd35b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java
@@ -50,6 +50,7 @@
     @Keep
     private final CarColor mCarColor;
 
+    /** Returns the {@link CarColor} associated with this span or {@code null} if not set. */
     @Nullable
     public CarColor getColor() {
         return mCarColor;
@@ -60,8 +61,8 @@
      *
      * <p>Custom colors created with {@link CarColor#createCustom} are not supported in text spans.
      *
-     * @throws IllegalArgumentException if {@code carColor} contains a custom color.
-     * @throws NullPointerException     if {@code carColor} is {@code null}.
+     * @throws IllegalArgumentException if {@code carColor} contains a custom color
+     * @throws NullPointerException     if {@code carColor} is {@code null}
      */
     @NonNull
     public static ForegroundCarColorSpan create(@NonNull CarColor carColor) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/GridItem.java b/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
index b6552d3..e348fb3 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
@@ -44,7 +44,6 @@
      *
      * @hide
      */
-    // TODO(shiufai): investigate how to expose IntDefs if needed.
     @RestrictTo(LIBRARY)
     @IntDef(value = {IMAGE_TYPE_ICON, IMAGE_TYPE_LARGE})
     @Retention(RetentionPolicy.SOURCE)
@@ -55,8 +54,7 @@
      * Represents an icon to be displayed in the grid item.
      *
      * <p>If necessary, icons will be scaled down to fit within a 64 x 64 dp bounding box,
-     * preserving
-     * their aspect ratios.
+     * preserving their aspect ratios.
      *
      * <p>A tint color is expected to be provided via {@link CarIcon.Builder#setTint}. Otherwise, a
      * default tint color as determined by the host will be applied.
@@ -89,24 +87,41 @@
     @Nullable
     private final OnClickDelegate mOnClickDelegate;
 
-    /** Returns whether the grid item is loading. */
+    /**
+     * Returns whether the grid item is in a loading state.
+     *
+     * @see Builder#setLoading(boolean)
+     */
     public boolean isLoading() {
         return mIsLoading;
     }
 
-    /** Returns the title of the grid item. */
+    /**
+     * Returns the title of the grid item or {@code null} if not set.
+     *
+     * @see Builder#setTitle(CharSequence)
+     */
     @Nullable
     public CarText getTitle() {
         return mTitle;
     }
 
-    /** Returns the list of text below the title. */
+    /**
+     * Returns the text to display below the title or {@code null} if no text will be displayed
+     * below the title.
+     *
+     * @see Builder#setText(CharSequence)
+     */
     @Nullable
     public CarText getText() {
         return mText;
     }
 
-    /** Returns the image of the grid item. */
+    /**
+     * Returns the image of the grid item or {@code null} if not set.
+     *
+     * @see Builder#setImage(CarIcon)
+     */
     @Nullable
     public CarIcon getImage() {
         return mImage;
@@ -119,7 +134,7 @@
     }
 
     /**
-     * Returns the {@link OnClickDelegate} to be called back when the grid item is clicked, or
+     * Returns the {@link OnClickDelegate} to be called back when the grid item is clicked or
      * {@code null} if the grid item is non-clickable.
      */
     @Nullable
@@ -216,8 +231,8 @@
          *
          * <p>Unless set with this method, the grid item will not have an title.
          *
-         * @throws NullPointerException     if {@code title} is {@code null}.
-         * @throws IllegalArgumentException if {@code title} is empty.
+         * @throws NullPointerException     if {@code title} is {@code null}
+         * @throws IllegalArgumentException if {@code title} is empty
          */
         @NonNull
         public Builder setTitle(@NonNull CharSequence title) {
@@ -236,9 +251,9 @@
          *
          * <h2>Text Wrapping</h2>
          *
-         * This text is truncated at the end to fit in a single line below the title.
+         * This text is truncated at the end to fit in a single line below the title
          *
-         * @throws NullPointerException if {@code text} is {@code null}.
+         * @throws NullPointerException if {@code text} is {@code null}
          */
         @NonNull
         public Builder setText(@NonNull CharSequence text) {
@@ -249,7 +264,8 @@
         /**
          * Sets an image to show in the grid item with the default size {@link #IMAGE_TYPE_LARGE}.
          *
-         * @throws NullPointerException if {@code image} is {@code null}.
+         * @throws NullPointerException if {@code image} is {@code null}
+         *
          * @see #setImage(CarIcon, int)
          */
         @NonNull
@@ -274,9 +290,10 @@
          * <p>See {@link CarIcon} for more details related to providing icon and image resources
          * that work with different car screen pixel densities.
          *
-         * @param image     the {@link CarIcon} to display.
-         * @param imageType one of {@link #IMAGE_TYPE_ICON} or {@link #IMAGE_TYPE_LARGE}.
-         * @throws NullPointerException if {@code image} is {@code null}.
+         * @param image     the {@link CarIcon} to display
+         * @param imageType one of {@link #IMAGE_TYPE_ICON} or {@link #IMAGE_TYPE_LARGE}
+         *
+         * @throws NullPointerException if {@code image} is {@code null}
          */
         @NonNull
         public Builder setImage(@NonNull CarIcon image, @GridItemImageType int imageType) {
@@ -291,9 +308,9 @@
          * {@code null} to make the grid item non-clickable.
          *
          * <p>Note that the listener relates to UI events and will be executed on the main thread
-         * using {@link Looper#getMainLooper()}.
+         * using {@link Looper#getMainLooper()}
          *
-         * @throws NullPointerException if {@code onClickListener} is {@code null}.
+         * @throws NullPointerException if {@code onClickListener} is {@code null}
          */
         @NonNull
         @SuppressLint({"MissingGetterMatchingBuilder", "ExecutorRegistration"})
@@ -307,7 +324,7 @@
          *
          * @throws IllegalStateException if the grid item's title is not set, if the grid item's
          *                               image is set when it is loading or vice versa, or if
-         *                               the grid item is loading but the click listener is set.
+         *                               the grid item is loading but the click listener is set
          */
         @NonNull
         public GridItem build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
index 51dee65..e4dbc3e 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
@@ -59,30 +59,57 @@
     @Nullable
     private final ActionStrip mActionStrip;
 
-    public boolean isLoading() {
-        return mIsLoading;
-    }
-
+    /**
+     * Returns the title of the template or {@code null} if not set.
+     *
+     * @see Builder#setTitle(CharSequence)
+     */
     @Nullable
     public CarText getTitle() {
         return mTitle;
     }
 
+    /**
+     * Returns the {@link Action} that is set to be displayed in the header of the template, or
+     * {@code null} if not set.
+     *
+     * @see Builder#setHeaderAction(Action)
+     */
     @Nullable
     public Action getHeaderAction() {
         return mHeaderAction;
     }
 
-    @Nullable
-    public ItemList getSingleList() {
-        return mSingleList;
-    }
-
+    /**
+     * Returns the {@link ActionStrip} for this template or {@code null} if not set.
+     *
+     * @see Builder#setActionStrip(ActionStrip)
+     */
     @Nullable
     public ActionStrip getActionStrip() {
         return mActionStrip;
     }
 
+    /**
+     * Returns whether the template is loading.
+     *
+     * @see Builder#setLoading(boolean)
+     */
+    public boolean isLoading() {
+        return mIsLoading;
+    }
+
+    /**
+     * Returns the {@link ItemList} instance that contains the grid items to display or {@code
+     * null} if not set.
+     *
+     * @see Builder#setSingleList(ItemList)
+     */
+    @Nullable
+    public ItemList getSingleList() {
+        return mSingleList;
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -156,8 +183,7 @@
         }
 
         /**
-         * Sets the {@link Action} that will be displayed in the header of the template, or
-         * {@code null} to not display an action.
+         * Sets the {@link Action} that will be displayed in the header of the template.
          *
          * <p>Unless set with this method, the template will not have a header action.
          *
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ItemList.java b/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
index 0bd9c1f..9721102 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
@@ -50,7 +50,7 @@
          * <p>This event is called even if the selection did not change, for example, if the user
          * selected an already selected item.
          *
-         * @param selectedIndex the index of the newly selected item.
+         * @param selectedIndex the index of the newly selected item
          */
         void onSelected(int selectedIndex);
     }
@@ -64,9 +64,9 @@
          * first item in a list is visible, the start and end indices would be 0 and 1,
          * respectively. If no items are visible, the indices will be set to -1.
          *
-         * @param startIndex the index of the first item that is visible.
+         * @param startIndex the index of the first item that is visible
          * @param endIndex   the index of the first item that is not visible after the visible
-         *                   range.
+         *                   range
          */
         void onItemVisibilityChanged(int startIndex, int endIndex);
     }
@@ -85,21 +85,32 @@
     @Nullable
     private final CarText mNoItemsMessage;
 
-    /** Returns the index of the selected item of the list. */
+    /**
+     * Returns the index of the selected item of the list.
+     *
+     * @see Builder#setSelectedIndex(int)
+     */
     public int getSelectedIndex() {
         return mSelectedIndex;
     }
 
     /**
      * Returns the {@link OnSelectedDelegate} to be called when when an item is selected
-     * by the user, or {@code null} is the list is non-selectable.
+     * by the user or {@code null} is the list is non-selectable.
+     *
+     * @see Builder#setOnSelectedListener(OnSelectedListener)
      */
     @Nullable
     public OnSelectedDelegate getOnSelectedDelegate() {
         return mOnSelectedDelegate;
     }
 
-    /** Returns the text to be displayed if the list is empty. */
+    /**
+     * Returns the app-supplied text to be displayed if the list is empty or {@code null} if the
+     * default text will be used by the host.
+     *
+     * @see Builder#setNoItemsMessage(CharSequence)
+     */
     @Nullable
     public CarText getNoItemsMessage() {
         return mNoItemsMessage;
@@ -107,7 +118,9 @@
 
     /**
      * Returns the {@link OnItemVisibilityChangedDelegate} to be called when the visible
-     * items in the list changes.
+     * items in the list changes or {@code null} if one hasn't been set.
+     *
+     * @see Builder#setOnItemsVisibilityChangedListener(OnItemVisibilityChangedListener)
      */
     @Nullable
     public OnItemVisibilityChangedDelegate getOnItemVisibilityChangedDelegate() {
@@ -116,24 +129,14 @@
 
     /**
      * Returns the list of items in this {@link ItemList}.
+     *
+     * @see Builder#addItem(Item)
      */
     @NonNull
     public List<Item> getItems() {
         return CollectionUtils.emptyIfNull(mItems);
     }
 
-    /**
-     * Returns the list of items in this {@link ItemList}.
-     *
-     * @deprecated use {@link #getItems()} instead.
-     */
-    // TODO(b/177591128): remove after host(s) no longer reference this.
-    @Deprecated
-    @NonNull
-    public List<Item> getItemList() {
-        return CollectionUtils.emptyIfNull(mItems);
-    }
-
     @Override
     @NonNull
     public String toString() {
@@ -257,6 +260,7 @@
          * radio button group, while others may highlight the selected item's background.
          *
          * @throws NullPointerException if {@code onSelectedListener} is {@code null}
+         *
          * @see #setSelectedIndex(int)
          */
         @NonNull
@@ -301,7 +305,7 @@
         /**
          * Adds an item to the list.
          *
-         * @throws NullPointerException if {@code item} is {@code null}.
+         * @throws NullPointerException if {@code item} is {@code null}
          */
         @NonNull
         public Builder addItem(@NonNull Item item) {
@@ -316,7 +320,7 @@
          *                               the selected index is greater or equal to the size of the
          *                               list, or if the list is selectable and any items have
          *                               either one of their {@link OnClickListener} or
-         *                               {@link Toggle} set.
+         *                               {@link Toggle} set
          */
         @NonNull
         public ItemList build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java
index 2adbe26..6e2e03a 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java
@@ -22,8 +22,6 @@
 
 import static java.util.Objects.requireNonNull;
 
-import android.annotation.SuppressLint;
-
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -72,45 +70,67 @@
     @Nullable
     private final ActionStrip mActionStrip;
 
-    public boolean isLoading() {
-        return mIsLoading;
-    }
-
+    /**
+     * Returns the title of the template or {@code null} if not set.
+     *
+     * @see Builder#setTitle(CharSequence)
+     */
     @Nullable
     public CarText getTitle() {
         return mTitle;
     }
 
+    /**
+     * Returns the {@link Action} that is set to be displayed in the header of the template, or
+     * {@code null} if not set.
+     *
+     * @see Builder#setHeaderAction(Action)
+     */
     @Nullable
     public Action getHeaderAction() {
         return mHeaderAction;
     }
 
+    /**
+     * Returns the {@link ActionStrip} for this template or {@code null} if not set.
+     *
+     * @see Builder#setActionStrip(ActionStrip)
+     */
+    @Nullable
+    public ActionStrip getActionStrip() {
+        return mActionStrip;
+    }
+
+    /**
+     * Returns whether the template is loading.
+     *
+     * @see Builder#setLoading(boolean)
+     */
+    public boolean isLoading() {
+        return mIsLoading;
+    }
+
+    /**
+     * Returns the {@link ItemList} instance containing the list of items to display or {@code
+     * null} if one hasn't been set.
+     *
+     * @see Builder#setSingleList(ItemList)
+     */
     @Nullable
     public ItemList getSingleList() {
         return mSingleList;
     }
 
     /**
-     * @deprecated use {@link #getSectionedLists()} instead.
+     * Returns the list of {@link SectionedItemList} instances to be displayed in the template.
+     *
+     * @see Builder#addSectionedList(SectionedItemList)
      */
-    // TODO(b/177591128): remove after host(s) no longer reference this.
-    @Deprecated
-    @NonNull
-    public List<SectionedItemList> getSectionLists() {
-        return CollectionUtils.emptyIfNull(mSectionedLists);
-    }
-
     @NonNull
     public List<SectionedItemList> getSectionedLists() {
         return CollectionUtils.emptyIfNull(mSectionedLists);
     }
 
-    @Nullable
-    public ActionStrip getActionStrip() {
-        return mActionStrip;
-    }
-
     @NonNull
     @Override
     public String toString() {
@@ -183,7 +203,7 @@
          * to the host once the data is ready.
          *
          * <p>If set to {@code false}, the UI will display the contents of the {@link ItemList}
-         * instance(s) added via {@link #setSingleList} or {@link #addList}.
+         * instance(s) added via {@link #setSingleList} or {@link #addSectionedList}.
          */
         @NonNull
         public Builder setLoading(boolean isLoading) {
@@ -230,12 +250,10 @@
         /**
          * Sets a single {@link ItemList} to show in the template.
          *
-         * <p>Note that this list cannot be mixed with others added via {@link #addList}. If
-         * multiple lists were previously added, they will be cleared.
+         * <p>Note that this list cannot be mixed with others added via {@link #addSectionedList}
+         * . If multiple lists were previously added, they will be cleared.
          *
-         * @throws NullPointerException if {@code list} is null.
-         *
-         * @see #addList(ItemList, CharSequence)
+         * @throws NullPointerException if {@code list} is null
          */
         @NonNull
         public Builder setSingleList(@NonNull ItemList list) {
@@ -246,34 +264,6 @@
         }
 
         /**
-         * Adds an {@link ItemList} to display in the template.
-         *
-         * <p>Use this method to add multiple {@link ItemList}s to the template. Each
-         * {@link ItemList} will be grouped under the given {@code header}. These lists cannot be
-         * mixed with an {@link ItemList} added via {@link #setSingleList}. If a single list was
-         * previously added, it will be cleared.
-         *
-         * <p>If the added {@link ItemList} contains a {@link ItemList.OnSelectedListener}, then it
-         * cannot be added alongside other {@link ItemList}(s).
-         *
-         * @throws NullPointerException     if {@code list} or {@code header} is {@code null}.
-         * @throws IllegalArgumentException if {@code list} is empty, if {@code list}'s {@link
-         *                                  ItemList.OnItemVisibilityChangedListener} is set, if
-         *                                  {@code header} is empty, or if a selectable list is
-         *                                  added alongside other lists.
-         *
-         * @deprecated use {@link #addSectionedList}  instead.
-         */
-        // TODO(b/177591128): remove after host(s) no longer reference this.
-        @Deprecated
-        @NonNull
-        // TODO(shiufai): consider rename to match getter's name.
-        @SuppressLint("MissingGetterMatchingBuilder")
-        public Builder addList(@NonNull ItemList list, @NonNull CharSequence header) {
-            return addSectionedList(SectionedItemList.create(list, header));
-        }
-
-        /**
          * Adds an {@link SectionedItemList} to display in the template.
          *
          * <p>Use this method to add multiple lists to the template. Each
@@ -285,11 +275,11 @@
          * {@link ItemList.OnSelectedListener}, then it cannot be added alongside other
          * {@link SectionedItemList}(s).
          *
-         * @throws NullPointerException     if {@code list} or {@code header} is {@code null}.
+         * @throws NullPointerException     if {@code list} or {@code header} is {@code null}
          * @throws IllegalArgumentException if {@code list} is empty, if {@code list}'s {@link
          *                                  ItemList.OnItemVisibilityChangedListener} is set, if
          *                                  {@code header} is empty, or if a selectable list is
-         *                                  added alongside other lists.
+         *                                  added alongside other lists
          */
         @NonNull
         public Builder addSectionedList(@NonNull SectionedItemList list) {
@@ -320,7 +310,7 @@
         }
 
         /**
-         * Sets the {@link ActionStrip} for this template, or {@code null} to not display an {@link
+         * Sets the {@link ActionStrip} for this template or {@code null} to not display an {@link
          * ActionStrip}.
          *
          * <p>Unless set with this method, the template will not have an action strip.
@@ -331,7 +321,7 @@
          * {@link Action}s, one of them can contain a title as set via
          * {@link Action.Builder#setTitle}. Otherwise, only {@link Action}s with icons are allowed.
          *
-         * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements.
+         * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements
          * @throws NullPointerException     if {@code actionStrip} is {@code null}
          */
         @NonNull
@@ -354,9 +344,9 @@
          *
          * @throws IllegalStateException    if the template is in a loading state but there are
          *                                  lists added or vice versa, or if the template does
-         *                                  not have either a title or header {@link Action} set.
+         *                                  not have either a title or header {@link Action} set
          * @throws IllegalArgumentException if the added {@link ItemList}(s) do not meet the
-         *                                  template's requirements.
+         *                                  template's requirements
          */
         @NonNull
         public ListTemplate build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
index bb1b0b2..6c0a53f 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
@@ -61,31 +61,63 @@
     @Keep
     private final List<Action> mActionList;
 
+    /**
+     * Returns the title of the template or {@code null} if not set.
+     *
+     * @see Builder#setTitle(CharSequence)
+     */
     @Nullable
     public CarText getTitle() {
         return mTitle;
     }
 
+    /**
+     * Returns the {@link Action} that is set to be displayed in the header of the template, or
+     * {@code null} if not set.
+     *
+     * @see Builder#setHeaderAction(Action)
+     */
     @Nullable
     public Action getHeaderAction() {
         return mHeaderAction;
     }
 
+    /**
+     * Returns the message to display in the template.
+     *
+     * @see Builder#Builder(CharSequence)
+     */
     @Nullable
     public CarText getMessage() {
         return mMessage;
     }
 
+    /**
+     * Returns a debug message to display in the template or {@code null} if not set.
+     *
+     * @see Builder#setDebugMessage(Throwable)
+     * @see Builder#setDebugMessage(String)
+     */
     @Nullable
     public CarText getDebugMessage() {
         return mDebugMessage;
     }
 
+    /**
+     * Returns the icon to display in the template or {@code null} if not set.
+     *
+     * @see Builder#setIcon(CarIcon)
+     */
     @Nullable
     public CarIcon getIcon() {
         return mIcon;
     }
 
+    /**
+     * Returns the list of actions to display in the template.
+     *
+     * @see Builder#addAction(Action)
+     */
     @NonNull
     public List<Action> getActions() {
         return CollectionUtils.emptyIfNull(mActionList);
@@ -161,7 +193,7 @@
          *
          * <p>Unless set with this method, the template will not have a title.
          *
-         * @throws NullPointerException if {@code title} is null
+         * @throws NullPointerException if {@code title} is {@code null}
          */
         @NonNull
         public Builder setTitle(@NonNull CharSequence title) {
@@ -170,7 +202,7 @@
         }
 
         /**
-         * Sets a {@link Throwable} for debugging purposes, or {@code null} to not show it.
+         * Sets a {@link Throwable} for debugging purposes.
          *
          * <p>The cause will be displayed along with the message set in
          * {@link #setDebugMessage(String)}.
@@ -188,7 +220,7 @@
         }
 
         /**
-         * Sets a debug message for debugging purposes, or {@code null} to not show a debug message.
+         * Sets a debug message for debugging purposes.
          *
          * <p>The debug message will be displayed along with the cause set in
          * {@link #setDebugMessage}.
@@ -229,8 +261,7 @@
         }
 
         /**
-         * Sets the {@link Action} that will be displayed in the header of the template, or
-         * {@code null} to not display an action.
+         * Sets the {@link Action} that will be displayed in the header of the template.
          *
          * <p>Unless set with this method, the template will not have a header action.
          *
@@ -256,7 +287,7 @@
          *
          * <p>Any actions above the maximum limit of 2 will be ignored.
          *
-         * @throws NullPointerException if {@code action} is {@code null}.
+         * @throws NullPointerException if {@code action} is {@code null}
          */
         @NonNull
         public Builder addAction(@NonNull Action action) {
@@ -275,7 +306,7 @@
          * <p>Either a header {@link Action} or title must be set on the template.
          *
          * @throws IllegalStateException if the message is empty, or if the template does not have
-         *                               either a title or header {@link Action} set.
+         *                               either a title or header {@link Action} set
          */
         @NonNull
         public MessageTemplate build() {
@@ -302,8 +333,9 @@
         /**
          * Returns a {@link Builder} instance.
          *
-         * @param message the text message to display in the template.
-         * @throws NullPointerException if the {@code message} is {@code null}.
+         * @param message the text message to display in the template
+         *
+         * @throws NullPointerException if the {@code message} is {@code null}
          */
         public Builder(@NonNull CharSequence message) {
             this.mMessage = CarText.create(requireNonNull(message));
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Metadata.java b/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
index e5ff857..ec59bc7 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
@@ -33,6 +33,11 @@
     @Nullable
     private final Place mPlace;
 
+    /**
+     * Returns a {@link Place} instance set in the metadata.
+     *
+     * @see Builder#setPlace(Place)
+     */
     @Nullable
     public Place getPlace() {
         return mPlace;
@@ -72,7 +77,7 @@
 
         /**
          * Sets a {@link Place} used for showing {@link Distance} and {@link PlaceMarker}
-         * information, or {@code null} if no {@link Place} information is available.
+         * information.
          *
          * @throws NullPointerException if {@code place} is {@code null}
          */
@@ -97,7 +102,7 @@
         /**
          * Returns a new {@link Builder} with the data from the given {@link Metadata} instance.
          *
-         * @throws NullPointerException if {@code icon} is {@code null}.
+         * @throws NullPointerException if {@code icon} is {@code null}
          */
         public Builder(@NonNull Metadata metadata) {
             this.mPlace = requireNonNull(metadata).getPlace();
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ModelUtils.java b/car/app/app/src/main/java/androidx/car/app/model/ModelUtils.java
index 67bcd64..09c5203 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ModelUtils.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ModelUtils.java
@@ -39,7 +39,7 @@
      *
      * @throws IllegalArgumentException if the input list contains any non-Row instances, or if any
      *                                  non-browsable row does not have a {@link DistanceSpan}
-     *                                  instance.
+     *                                  instance
      */
     public static void validateAllNonBrowsableRowsHaveDistance(@NonNull List<Item> rows) {
         int spanSetCount = 0;
@@ -72,7 +72,7 @@
      *
      * @throws IllegalArgumentException if the input list contains any non-Row instances, or if any
      *                                  non-browsable row does not have either a {@link
-     *                                  DurationSpan} or {@link DistanceSpan} instance.
+     *                                  DurationSpan} or {@link DistanceSpan} instance
      */
     public static void validateAllRowsHaveDistanceOrDuration(@NonNull List<Item> rows) {
         for (Item rowObj : rows) {
@@ -94,7 +94,7 @@
      * Checks whether all rows have only small-sized images if they are set.
      *
      * @throws IllegalArgumentException if the input list contains any non-Row instances, or if an
-     *                                  image set in any rows is using {@link Row#IMAGE_TYPE_LARGE}.
+     *                                  image set in any rows is using {@link Row#IMAGE_TYPE_LARGE}
      */
     public static void validateAllRowsHaveOnlySmallImages(@NonNull List<Item> rows) {
         for (Item rowObj : rows) {
@@ -112,7 +112,7 @@
      * Checks whether any rows have both a marker and an image.
      *
      * @throws IllegalArgumentException if the input list contains any non-Row instances, or if
-     *                                  both a marker and an image are set in a row.
+     *                                  both a marker and an image are set in a row
      */
     public static void validateNoRowsHaveBothMarkersAndImages(@NonNull List<Item> rows) {
         for (Item rowObj : rows) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegate.java
index 078fc40..b63b535 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegate.java
@@ -28,9 +28,9 @@
     /**
      * Notifies that checked state has changed.
      *
-     * @param isChecked the updated checked state.
+     * @param isChecked the updated checked state
      * @param callback  the {@link OnDoneCallback} to trigger when the client finishes handling
-     *                  the event.
+     *                  the event
      */
     // This mirrors the AIDL class and is not supported to support an executor as an input.
     @SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegate.java
index 41a64e8..7993740 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegate.java
@@ -34,7 +34,7 @@
      * Notifies that a click happened.
      *
      * @param callback the {@link OnDoneCallback} to trigger when the client finishes handling
-     *                 the event.
+     *                 the event
      */
     // This mirrors the AIDL class and is not supported to support an executor as an input.
     @SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegate.java
index 6b2e423..f3ef897 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegate.java
@@ -33,11 +33,11 @@
      * respectively. If no items are visible, the indices will be set to -1.
      *
      * @param startIndex the index (inclusive) of the first visible element, or -1 if no items
-     *                   are visible.
+     *                   are visible
      * @param endIndex   the index (exclusive) of the last visible element, or -1 if no items
-     *                   are visible.
+     *                   are visible
      * @param callback   the {@link OnDoneCallback} to trigger when the client finishes handling
-     *                   the event.
+     *                   the event
      */
     // This mirrors the AIDL class and is not supported to support an executor as an input.
     @SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegate.java
index 3d955ca..72ba285 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegate.java
@@ -31,9 +31,9 @@
      * <p>This event is called even if the selection did not change, for example, if the user
      * selected an already selected item.
      *
-     * @param selectedIndex the index of the selected item.
+     * @param selectedIndex the index of the selected item
      * @param callback      the {@link OnDoneCallback} to trigger when the client finishes handling
-     *                      the event.
+     *                      the event
      */
     // This mirrors the AIDL class and is not supported to support an executor as an input.
     @SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Pane.java b/car/app/app/src/main/java/androidx/car/app/model/Pane.java
index 8b1bfd5..d1beb9a 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Pane.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Pane.java
@@ -41,6 +41,15 @@
     private final boolean mIsLoading;
 
     /**
+     * Returns whether the pane is in a loading state.
+     *
+     * @see Builder#setLoading(boolean)
+     */
+    public boolean isLoading() {
+        return mIsLoading;
+    }
+
+    /**
      * Returns the list of {@link Action}s displayed alongside the {@link Row}s in this pane.
      */
     @NonNull
@@ -56,25 +65,6 @@
         return CollectionUtils.emptyIfNull(mRows);
     }
 
-    /**
-     * Returns the list of {@link Row} objects that make up the {@link Pane}.
-     *
-     * @deprecated use {@link #getRows()} instead.
-     */
-    // TODO(b/177591128): remove after host(s) no longer reference this.
-    @Deprecated
-    @NonNull
-    public List<Row> getRowList() {
-        return CollectionUtils.emptyIfNull(mRows);
-    }
-
-    /**
-     * Returns the {@code true} if the {@link Pane} is loading.*
-     */
-    public boolean isLoading() {
-        return mIsLoading;
-    }
-
     @Override
     @NonNull
     public String toString() {
@@ -144,7 +134,7 @@
         /**
          * Adds a row to display in the list.
          *
-         * @throws NullPointerException if {@code row} is {@code null}.
+         * @throws NullPointerException if {@code row} is {@code null}
          */
         @NonNull
         public Builder addRow(@NonNull Row row) {
@@ -157,7 +147,7 @@
          *
          * <p>By default, no actions are displayed.
          *
-         * @throws NullPointerException if {@code action} is {@code null}.
+         * @throws NullPointerException if {@code action} is {@code null}
          */
         @NonNull
         public Builder addAction(@NonNull Action action) {
@@ -170,7 +160,7 @@
          * Constructs the row list defined by this builder.
          *
          * @throws IllegalStateException if the pane is in loading state and also contains rows, or
-         *                               vice versa.
+         *                               vice versa
          */
         @NonNull
         public Pane build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java
index 4d9de4e..a065cbf 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java
@@ -59,26 +59,47 @@
     @Nullable
     private final ActionStrip mActionStrip;
 
+    /**
+     * Returns the title of the template or {@code null} if not set.
+     *
+     * @see Builder#setTitle(CharSequence)
+     */
     @Nullable
     public CarText getTitle() {
         return mTitle;
     }
 
+    /**
+     * Returns the {@link Action} that is set to be displayed in the header of the template, or
+     * {@code null} if not set.
+     *
+     * @see Builder#setHeaderAction(Action)
+     */
     @Nullable
     public Action getHeaderAction() {
         return mHeaderAction;
     }
 
-    @Nullable
-    public Pane getPane() {
-        return mPane;
-    }
-
+    /**
+     * Returns the {@link ActionStrip} for this template or {@code null} if not set.
+     *
+     * @see Builder#setActionStrip(ActionStrip)
+     */
     @Nullable
     public ActionStrip getActionStrip() {
         return mActionStrip;
     }
 
+    /**
+     * Returns the {@link Pane} to display in the template or {@code null} if not set.
+     *
+     * @see Builder#Builder(Pane)
+     */
+    @Nullable
+    public Pane getPane() {
+        return mPane;
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -136,7 +157,7 @@
          *
          * <p>Unless set with this method, the template will not have a title.
          *
-         * @throws NullPointerException if {@code title} is null
+         * @throws NullPointerException if {@code title} is {@code null}
          */
         @NonNull
         public Builder setTitle(@NonNull CharSequence title) {
@@ -145,8 +166,7 @@
         }
 
         /**
-         * Sets the {@link Action} that will be displayed in the header of the template, or
-         * {@code null} to not display an action.
+         * Sets the {@link Action} that will be displayed in the header of the template.
          *
          * <p>Unless set with this method, the template will not have a header action.
          *
@@ -168,8 +188,7 @@
         }
 
         /**
-         * Sets the {@link ActionStrip} for this template, or {@code null} to not display an {@link
-         * ActionStrip}.
+         * Sets the {@link ActionStrip} for this template.
          *
          * <p>Unless set with this method, the template will not have an action strip.
          *
@@ -179,7 +198,7 @@
          * {@link Action}s, one of them can contain a title as set via
          * {@link Action.Builder#setTitle}. Otherwise, only {@link Action}s with icons are allowed.
          *
-         * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements.
+         * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements
          * @throws NullPointerException     if {@code actionStrip} is {@code null}
          */
         @NonNull
@@ -203,10 +222,9 @@
          *
          * <p>Either a header {@link Action} or title must be set on the template.
          *
-         * @throws IllegalArgumentException if the {@link Pane} does not meet the requirements.
+         * @throws IllegalArgumentException if the {@link Pane} does not meet the requirements
          * @throws IllegalStateException    if the template does not have either a title or header
-         *                                  {@link
-         *                                  Action} set.
+         *                                  {@link Action} set
          */
         @NonNull
         public PaneTemplate build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ParkedOnlyOnClickListener.java b/car/app/app/src/main/java/androidx/car/app/model/ParkedOnlyOnClickListener.java
index 0b6c942..08d2e14 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ParkedOnlyOnClickListener.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ParkedOnlyOnClickListener.java
@@ -47,6 +47,10 @@
     @Keep
     private final OnClickListener mListener;
 
+    /**
+     * Triggers the {@link OnClickListener#onClick()} method in the listener wrapped by this
+     * object.
+     */
     @Override
     public void onClick() {
         mListener.onClick();
@@ -58,7 +62,7 @@
      * <p>Note that the listener relates to UI events and will be executed on the main thread
      * using {@link Looper#getMainLooper()}.
      *
-     * @throws NullPointerException if {@code listener} is {@code null}.
+     * @throws NullPointerException if {@code listener} is {@code null}
      */
     @NonNull
     @SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Place.java b/car/app/app/src/main/java/androidx/car/app/model/Place.java
index 190ab06..a719a09 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Place.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Place.java
@@ -33,13 +33,22 @@
     @Nullable
     private final PlaceMarker mMarker;
 
+    /**
+     * Returns the {@link PlaceMarker} object associated with this place or {@code null} if one
+     * is not set.
+     *
+     * @see Builder#setMarker(PlaceMarker)
+     */
     @Nullable
     public PlaceMarker getMarker() {
         return mMarker;
     }
 
     /**
-     * @return the {@link CarLocation} set for this Place instance.
+     * Returns the {@link CarLocation} instance associated with this place or {@code null} if one
+     * is not set.
+     *
+     * @see Builder#Builder(CarLocation)
      */
     @Nullable
     public CarLocation getLocation() {
@@ -92,6 +101,7 @@
          * Returns a builder instance for a {@link CarLocation}.
          *
          * @param location the geographical location associated with the place
+         *
          * @throws NullPointerException if {@code location} is {@code null}
          */
         public Builder(@NonNull CarLocation location) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java
index 8556dce..d3664b3c 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java
@@ -74,30 +74,62 @@
         return mShowCurrentLocation;
     }
 
+    /**
+     * Returns the title of the template or {@code null} if not set.
+     *
+     * @see Builder#setTitle(CharSequence)
+     */
     @Nullable
     public CarText getTitle() {
         return mTitle;
     }
 
-    public boolean isLoading() {
-        return mIsLoading;
-    }
-
-    @Nullable
-    public ItemList getItemList() {
-        return mItemList;
-    }
-
+    /**
+     * Returns the {@link Action} that is set to be displayed in the header of the template, or
+     * {@code null} if not set.
+     *
+     * @see Builder#setHeaderAction(Action)
+     */
     @Nullable
     public Action getHeaderAction() {
         return mHeaderAction;
     }
 
+    /**
+     * Returns the {@link ActionStrip} for this template or {@code null} if not set.
+     *
+     * @see Builder#setActionStrip(ActionStrip)
+     */
     @Nullable
     public ActionStrip getActionStrip() {
         return mActionStrip;
     }
 
+    /**
+     * Returns whether the template is loading.
+     *
+     * @see Builder#setLoading(boolean)
+     */
+    public boolean isLoading() {
+        return mIsLoading;
+    }
+
+    /**
+     * Returns the {@link ItemList} instance with the list of items to display in the template,
+     * or {@code null} if not set.
+     *
+     * @see Builder#setItemList(ItemList)
+     */
+    @Nullable
+    public ItemList getItemList() {
+        return mItemList;
+    }
+
+    /**
+     * Returns the {@link Place} instance to display as an anchor in the map.
+     *
+     * @see Builder#setAnchor(Place)
+     */
     @Nullable
     public Place getAnchor() {
         return mAnchor;
@@ -202,8 +234,7 @@
         }
 
         /**
-         * Sets the {@link Action} that will be displayed in the header of the template, or
-         * {@code null} to not display an action.
+         * Sets the {@link Action} that will be displayed in the header of the template.
          *
          * <p>Unless set with this method, the template will not have a header action.
          *
@@ -229,7 +260,7 @@
          *
          * <p>Unless set with this method, the template will not have a title.
          *
-         * @throws NullPointerException if {@code title} is null
+         * @throws NullPointerException if {@code title} is {@code null}
          */
         @NonNull
         public Builder setTitle(@NonNull CharSequence title) {
@@ -278,8 +309,7 @@
         }
 
         /**
-         * Sets the {@link ActionStrip} for this template, or {@code null} to not display an {@link
-         * ActionStrip}.
+         * Sets the {@link ActionStrip} for this template.
          *
          * <p>Unless set with this method, the template will not have an action strip.
          *
@@ -289,7 +319,7 @@
          * {@link Action}s, one of them can contain a title as set via
          * {@link Action.Builder#setTitle}. Otherwise, only {@link Action}s with icons are allowed.
          *
-         * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements.
+         * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements
          * @throws NullPointerException     if {@code actionStrip} is {@code null}
          */
         @NonNull
@@ -329,9 +359,9 @@
          * Either a header {@link Action} or title must be set on the template.
          *
          * @throws IllegalArgumentException if the template is in a loading state but the list is
-         *                                  set, or vice versa.
+         *                                  set, or vice versa
          * @throws IllegalStateException    if the template does not have either a title or header
-         *                                  {@link Action} set.
+         *                                  {@link Action} set
          */
         @NonNull
         public PlaceListMapTemplate build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java b/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java
index 20c5597..778c467 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java
@@ -75,7 +75,7 @@
     private final int mIconType;
 
     /**
-     * Returns the {@link CarIcon} associated with this marker.
+     * Returns the {@link CarIcon} associated with this marker or {@code null} if not set.
      */
     @Nullable
     public CarIcon getIcon() {
@@ -91,11 +91,11 @@
     }
 
     /**
-     * If set, the text that should be rendered as the marker's content, {@code null} otherwise.
+     * Returns the text that should be rendered as the marker's content or {@code null} if one
+     * is not set.
      *
      * <p>Note that a {@link PlaceMarker} can only display either an icon or a text label. If
-     * both are
-     * set, then {@link #getIcon()} will take precedence.
+     * both are set, then {@link #getIcon()} will take precedence.
      */
     @Nullable
     public CarText getLabel() {
@@ -196,6 +196,7 @@
          *
          * @param icon     the {@link CarIcon} to display inside the marker
          * @param iconType one of {@link #TYPE_ICON} or {@link #TYPE_IMAGE}
+         *
          * @throws NullPointerException if the {@code icon} is {@code null}
          */
         @NonNull
@@ -215,7 +216,8 @@
          *
          * @param label the text to display inside of the marker. The string must have a maximum
          *              size of 3 characters. Set to {@code null} to let the host choose a
-         *              labelling scheme (for example, using a sequence of numbers).
+         *              labelling scheme (for example, using a sequence of numbers)
+         *
          * @throws NullPointerException if the {@code label} is {@code null}
          */
         @NonNull
@@ -239,7 +241,7 @@
          *   <li>When the {@link PlaceMarker} is displayed on the map, the pin enclosing the icon or
          *       label will be painted using the given color.
          *   <li>When the {@link PlaceMarker} is displayed on the list, the color will be applied
-         *       if the content is a label. A label rendered inside a map's pin cannot be color
+         *       if the content is a label. A label rendered inside a map's pin cannot be colored
          *       and will always use the default color as chosen by the host.
          * </ul>
          *
@@ -263,7 +265,7 @@
          * Constructs the {@link PlaceMarker} defined by this builder.
          *
          * @throws IllegalStateException if the icon is of the type {@link #TYPE_IMAGE} and a a
-         *                               color is set.
+         *                               color is set
          */
         @NonNull
         public PlaceMarker build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Row.java b/car/app/app/src/main/java/androidx/car/app/model/Row.java
index 648c994..3ed1bb2 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Row.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Row.java
@@ -51,7 +51,6 @@
      *
      * @hide
      */
-    // TODO(shiufai): investigate how to expose IntDefs if needed.
     @RestrictTo(LIBRARY)
     @IntDef(value = {IMAGE_TYPE_SMALL, IMAGE_TYPE_ICON, IMAGE_TYPE_LARGE})
     @Retention(RetentionPolicy.SOURCE)
@@ -78,8 +77,7 @@
      * Represents a small image to be displayed in the row.
      *
      * <p>If necessary, icons will be scaled down to fit within a 44 x 44 dp bounding box,
-     * preserving
-     * their aspect ratios.
+     * preserving their aspect ratios.
      *
      * <p>A tint color is expected to be provided via {@link CarIcon.Builder#setTint}. Otherwise, a
      * default tint color as determined by the host will be applied.
@@ -109,19 +107,33 @@
     @RowImageType
     private final int mRowImageType;
 
-    /** Returns the title of the row. */
+    /**
+     * Returns the title of the row or {@code null} if not set.
+     *
+     * @see Builder#setTitle(CharSequence)
+     */
     @Nullable
     public CarText getTitle() {
         return mTitle;
     }
 
-    /** Returns the list of text below the title. */
+    /**
+     * Returns the list of text below the title.
+     *
+     * @see Builder#addText(CharSequence)
+     */
     @NonNull
     public List<CarText> getTexts() {
         return CollectionUtils.emptyIfNull(mTexts);
     }
 
-    /** Returns the image of the row. */
+    /**
+     * Returns the image to display in the row or {@code null} if the row does not contain an
+     * image.
+     *
+     * @see Builder#setImage(CarIcon)
+     * @see Builder#setImage(CarIcon, int)
+     */
     @Nullable
     public CarIcon getImage() {
         return mImage;
@@ -136,6 +148,8 @@
     /**
      * Returns the {@link Toggle} in the row or {@code null} if the row does not contain a
      * toggle.
+     *
+     * @see Builder#setToggle(Toggle)
      */
     @Nullable
     public Toggle getToggle() {
@@ -143,16 +157,18 @@
     }
 
     /**
-     * Returns {@code true} if the row is browsable, {@code false} otherwise.
+     * Returns whether the row is browsable.
      *
      * <p>If a row is browsable, then no {@link Action} or {@link Toggle} can be added to it.
+     *
+     * @see Builder#isBrowsable()
      */
     public boolean isBrowsable() {
         return mIsBrowsable;
     }
 
     /**
-     * Returns the {@link OnClickListener} to be called back when the row is clicked, or {@code
+     * Returns the {@link OnClickListener} to be called back when the row is clicked or {@code
      * null} if the row is non-clickable.
      */
     @Nullable
@@ -161,7 +177,8 @@
     }
 
     /**
-     * Returns the {@link Metadata} associated with the row.
+     * Returns the {@link Metadata} associated with the row or {@code null} if there is no
+     * metadata associated with the row.
      */
     @Nullable
     public Metadata getMetadata() {
@@ -278,8 +295,8 @@
         /**
          * Sets the title of the row.
          *
-         * @throws NullPointerException     if {@code title} is {@code null}.
-         * @throws IllegalArgumentException if {@code title} is empty.
+         * @throws NullPointerException     if {@code title} is {@code null}
+         * @throws IllegalArgumentException if {@code title} is empty
          */
         @NonNull
         public Builder setTitle(@NonNull CharSequence title) {
@@ -352,7 +369,8 @@
          * of text
          * </pre>
          *
-         * @throws NullPointerException if {@code text} is {@code null}.
+         * @throws NullPointerException if {@code text} is {@code null}
+         *
          * @see ForegroundCarColorSpan
          */
         @NonNull
@@ -365,6 +383,7 @@
          * Sets an image to show in the row with the default size {@link #IMAGE_TYPE_SMALL}.
          *
          * @throws NullPointerException if {@code image} is {@code null}
+         *
          * @see #setImage(CarIcon, int)
          */
         @NonNull
@@ -389,9 +408,10 @@
          * <p>See {@link CarIcon} for more details related to providing icon and image resources
          * that work with different car screen pixel densities.
          *
-         * @param image     the {@link CarIcon} to display, or {@code null} to not display one.
+         * @param image     the {@link CarIcon} to display or {@code null} to not display one
          * @param imageType one of {@link #IMAGE_TYPE_ICON}, {@link #IMAGE_TYPE_SMALL} or {@link
          *                  #IMAGE_TYPE_LARGE}
+         *
          * @throws NullPointerException if {@code image} is {@code null}
          */
         @NonNull
@@ -428,8 +448,7 @@
         }
 
         /**
-         * Sets the {@link OnClickListener} to be called back when the row is clicked, or {@code
-         * null} to make the row non-clickable.
+         * Sets the {@link OnClickListener} to be called back when the row is clicked.
          *
          * <p>Note that the listener relates to UI events and will be executed on the main thread
          * using {@link Looper#getMainLooper()}.
@@ -448,7 +467,7 @@
          * Sets the {@link Metadata} associated with the row.
          *
          * @param metadata The metadata to set with the row. Pass {@link Metadata#EMPTY_METADATA}
-         *                 to not associate any metadata with the row.
+         *                 to not associate any metadata with the row
          */
         @NonNull
         public Builder setMetadata(@NonNull Metadata metadata) {
@@ -463,7 +482,7 @@
          *                               row and has a {@link Toggle}, if it is a browsable
          *                               row but does not have a {@link OnClickListener}, or if
          *                               it has both a {@link OnClickListener} and a {@link
-         *                               Toggle}.
+         *                               Toggle}
          */
         @NonNull
         public Row build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegate.java
index f928dee..15d6a23 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegate.java
@@ -28,9 +28,9 @@
     /**
      * Notifies that the search text has changed.
      *
-     * @param searchText the up-to-date search text.
+     * @param searchText the up-to-date search text
      * @param callback   the {@link OnDoneCallback} to trigger when the client finishes handling
-     *                   the event.
+     *                   the event
      */
     // This mirrors the AIDL class and is not supported to support an executor as an input.
     @SuppressLint("ExecutorRegistration")
@@ -39,9 +39,9 @@
     /**
      * Notifies that the user has submitted the search.
      *
-     * @param searchText the search text that was submitted.
+     * @param searchText the search text that was submitted
      * @param callback   the {@link OnDoneCallback} to trigger when the client finishes handling
-     *                   the event.
+     *                   the event
      */
     // This mirrors the AIDL class and is not supported to support an executor as an input.
     @SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java
index e25585f..30e01c3 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java
@@ -53,7 +53,7 @@
          * these updates is not guaranteed to be after every individual keystroke. The host may
          * decide to wait for several keystrokes before sending a single update.
          *
-         * @param searchText the current search text that the user has typed.
+         * @param searchText the current search text that the user has typed
          */
         void onSearchTextChanged(@NonNull String searchText);
 
@@ -61,7 +61,7 @@
          * Notifies that the user has submitted the search and the given {@code searchText} is
          * the final term.
          *
-         * @param searchText the search text that the user typed.
+         * @param searchText the search text that the user typed
          */
         void onSearchSubmitted(@NonNull String searchText);
     }
@@ -88,17 +88,21 @@
     @Nullable
     private final ActionStrip mActionStrip;
 
-    public boolean isLoading() {
-        return mIsLoading;
-    }
-
+    /**
+     * Returns the {@link Action} that is set to be displayed in the header of the template, or
+     * {@code null} if not set.
+     *
+     * @see Builder#setHeaderAction(Action)
+     */
     @Nullable
     public Action getHeaderAction() {
         return mHeaderAction;
     }
 
     /**
-     * Returns the {@link ActionStrip} instance set in the template.
+     * Returns the {@link ActionStrip} for this template or {@code null} if not set.
+     *
+     * @see Builder#setActionStrip(ActionStrip)
      */
     @Nullable
     public ActionStrip getActionStrip() {
@@ -106,6 +110,15 @@
     }
 
     /**
+     * Returns whether the template is loading.
+     *
+     * @see Builder#setLoading(boolean)
+     */
+    public boolean isLoading() {
+        return mIsLoading;
+    }
+
+    /**
      * Returns the optional initial search text.
      *
      * @see Builder#setInitialSearchText
@@ -126,7 +139,7 @@
     }
 
     /**
-     * Returns the optional {@link ItemList} for search results.
+     * Returns the {@link ItemList} for search results or {@code null} if not set.
      *
      * @see Builder#getItemList
      */
@@ -136,7 +149,8 @@
     }
 
     /**
-     * Returns the {@link SearchCallbackDelegate} for search callbacks.
+     * Returns the {@link SearchCallbackDelegate} for search callbacks or {@code null} if one is
+     * not set.
      */
     @Nullable
     public SearchCallbackDelegate getSearchCallbackDelegate() {
@@ -230,8 +244,7 @@
         ActionStrip mActionStrip;
 
         /**
-         * Sets the {@link Action} that will be displayed in the header of the template, or
-         * {@code null} to not display an action.
+         * Sets the {@link Action} that will be displayed in the header of the template.
          *
          * <p>Unless set with this method, the template will not have a header action.
          *
@@ -253,8 +266,7 @@
         }
 
         /**
-         * Sets the {@link ActionStrip} for this template, or {@code null} to not display an {@link
-         * ActionStrip}.
+         * Sets the {@link ActionStrip} for this template.
          *
          * <p>Unless set with this method, the template will not have an action strip.
          *
@@ -264,7 +276,7 @@
          * {@link Action}s, one of them can contain a title as set via
          * {@link Action.Builder#setTitle}. Otherwise, only {@link Action}s with icons are allowed.
          *
-         * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements.
+         * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements
          * @throws NullPointerException     if {@code actionStrip} is {@code null}
          */
         @NonNull
@@ -276,6 +288,7 @@
 
         /**
          * Sets the initial search text to display in the search box.
+         *
          * @throws NullPointerException if {@code initialSearchText} is {@code null}
          */
         @NonNull
@@ -287,7 +300,7 @@
         /**
          * Sets the text hint to display in the search box when it is empty.
          *
-         * <p>The host will use a default search hint if one is not set with this method.
+         * <p>The host will use a default search hint if not set with this method.
          *
          * <p>This is not the actual search text, and will disappear if user types any value into
          * the search.
@@ -358,7 +371,7 @@
          * Constructs the {@link SearchTemplate} model.
          *
          * @throws IllegalArgumentException if the template is in a loading state but the list is
-         *                                  set.
+         *                                  set
          */
         @NonNull
         public SearchTemplate build() {
@@ -377,7 +390,7 @@
          * using {@link Looper#getMainLooper()}.
          *
          * @param callback the callback to be invoked for events such as when the user types new
-         *                 text, or submits a search.
+         *                 text, or submits a search
          */
         @SuppressLint("ExecutorRegistration")
         public Builder(@NonNull SearchCallback callback) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/SectionedItemList.java b/car/app/app/src/main/java/androidx/car/app/model/SectionedItemList.java
index 19f3c32..419b56c 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/SectionedItemList.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/SectionedItemList.java
@@ -38,20 +38,6 @@
     /**
      * Creates an instance of a {@link SectionedItemList} with the given {@code itemList} and
      * {@code sectionHeader}.
-     *
-     * @deprecated use {@link #create(ItemList, CharSequence)} ()} instead.
-     */
-    // TODO(b/177591128): remove after host(s) no longer reference this.
-    @Deprecated
-    @NonNull
-    public static SectionedItemList create(
-            @NonNull ItemList itemList, @NonNull CarText sectionHeader) {
-        return new SectionedItemList(requireNonNull(itemList), requireNonNull(sectionHeader));
-    }
-
-    /**
-     * Creates an instance of a {@link SectionedItemList} with the given {@code itemList} and
-     * {@code sectionHeader}.
      */
     @NonNull
     public static SectionedItemList create(
@@ -60,13 +46,13 @@
                 CarText.create(requireNonNull(sectionHeader)));
     }
 
-    /** Returns the {@link ItemList} for the section. */
+    /** Returns the {@link ItemList} for the section or {@code null} if not set. */
     @Nullable
     public ItemList getItemList() {
         return mItemList;
     }
 
-    /** Returns the title of the section. */
+    /** Returns the title of the section or {@code null} if not set */
     @Nullable
     public CarText getHeader() {
         return mHeader;
@@ -102,7 +88,6 @@
         this.mHeader = header;
     }
 
-    /** For serialization. */
     private SectionedItemList() {
         this.mItemList = null;
         this.mHeader = null;
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TemplateInfo.java b/car/app/app/src/main/java/androidx/car/app/model/TemplateInfo.java
index 4d33ad2..148008c 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TemplateInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TemplateInfo.java
@@ -16,6 +16,8 @@
 
 package androidx.car.app.model;
 
+import static java.util.Objects.requireNonNull;
+
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -30,15 +32,17 @@
  */
 public final class TemplateInfo {
     @Keep
+    @Nullable
     private final Class<? extends Template> mTemplateClass;
     @Keep
+    @Nullable
     private final String mTemplateId;
 
     /**
      * Constructs the info for the given template information provided.
      *
      * @param templateClass the class of the template this info is for
-     * @param templateId the unique id for the template
+     * @param templateId    the unique id for the template
      */
     public TemplateInfo(@NonNull Class<? extends Template> templateClass,
             @NonNull String templateId) {
@@ -47,28 +51,27 @@
     }
 
     /**
-     * @deprecated this constructor is deprecated.
+     * Returns the type of template this instance contains.
+     *
+     * @see TemplateInfo#TemplateInfo(Class, String)
      */
-    // TODO(b/178104682): Remove this method.
-    @Deprecated
-    public TemplateInfo(@NonNull Template template, @NonNull String templateId) {
-        this.mTemplateClass = template.getClass();
-        this.mTemplateId = templateId;
-    }
-
-    private TemplateInfo() {
-        this.mTemplateClass = null;
-        this.mTemplateId = null;
-    }
-
     @NonNull
     public Class<? extends Template> getTemplateClass() {
-        return mTemplateClass;
+        // Intentionally kept as non-null because the library creates these classes internally after
+        // the app returns a non-null template, a null-value should not be expected here.
+        return requireNonNull(mTemplateClass);
     }
 
+    /**
+     * Returns the ID of the template.
+     *
+     * @see TemplateInfo#TemplateInfo(Class, String)
+     */
     @NonNull
     public String getTemplateId() {
-        return mTemplateId;
+        // Intentionally kept as non-null because the library creates these classes internally after
+        // the app returns a non-null template, a null-value should not be expected here.
+        return requireNonNull(mTemplateId);
     }
 
     @Override
@@ -89,4 +92,9 @@
         return Objects.equals(mTemplateClass, otherInfo.mTemplateClass)
                 && Objects.equals(mTemplateId, otherInfo.mTemplateId);
     }
+
+    private TemplateInfo() {
+        this.mTemplateClass = null;
+        this.mTemplateId = null;
+    }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TemplateWrapper.java b/car/app/app/src/main/java/androidx/car/app/model/TemplateWrapper.java
index d3c5d88..699708e 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TemplateWrapper.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TemplateWrapper.java
@@ -22,6 +22,7 @@
 
 import androidx.annotation.Keep;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.car.app.utils.CollectionUtils;
 
@@ -41,19 +42,18 @@
  */
 public final class TemplateWrapper {
     @Keep
+    @Nullable
     private Template mTemplate;
     @Keep
+    @Nullable
     private String mId;
     @Keep
     private List<TemplateInfo> mTemplateInfoForScreenStack = new ArrayList<>();
 
-    /** The current step in a task that the template is in. For internal, host-side use only. */
+    /** The current step in a task that the template is in. */
     private int mCurrentTaskStep;
 
-    /**
-     * Whether the template wrapper is a refresh of the current template. For internal, host-side
-     * use only.
-     */
+    /** Whether the template wrapper is a refresh of the current template. */
     private boolean mIsRefresh;
 
     /**
@@ -80,14 +80,12 @@
      * Creates a {@link TemplateWrapper} instance with the given {@link Template} and ID.
      *
      * <p>The ID is primarily used to inform the host that the given {@link Template} shares the
-     * same
-     * ID as a previously sent {@link Template}, even though their contents differ. In such
-     * cases, the
-     * host will reset the task step to where the previous {@link Template} was.
+     * same ID as a previously sent {@link Template}, even though their contents differ. In such
+     * cases, the host will reset the task step to where the previous {@link Template} was.
      *
-     * <p>For example, the client sends Template A (task step 1), then move forwards a screen and
-     * sends Template B (task step 2). Now the client pops the screen and sends Template C. By
-     * assigning the ID of Template A to Template C, the client library informs the host that it
+     * <p>For example, the client sends template A (task step 1), then move forwards a screen and
+     * sends template B (task step 2). Now the client pops the screen and sends template C. By
+     * assigning the ID of template A to template C, the client library informs the host that it
      * is a back operation and the task step should be set to 1 again.
      */
     @NonNull
@@ -98,13 +96,17 @@
     /** Returns the wrapped {@link Template}. */
     @NonNull
     public Template getTemplate() {
-        return mTemplate;
+        // Intentionally kept as non-null because the library creates these classes internally after
+        // the app returns a non-null template, a null-value should not be expected here.
+        return requireNonNull(mTemplate);
     }
 
     /** Returns the ID associated with the wrapped {@link Template}. */
     @NonNull
     public String getId() {
-        return mId;
+        // Intentionally kept as non-null because the library creates these classes internally after
+        // the app returns a non-null template, a null-value should not be expected here.
+        return requireNonNull(mId);
     }
 
     /**
@@ -125,24 +127,19 @@
      * screen stack managed by the screen manager.
      *
      * <p>The return values are in order, where position 0 is the top of the stack, and position
-     * n is
-     * the bottom of the stack given n screens on the stack.
+     * n is the bottom of the stack given n screens on the stack.
      */
     @NonNull
     public List<TemplateInfo> getTemplateInfosForScreenStack() {
         return CollectionUtils.emptyIfNull(mTemplateInfoForScreenStack);
     }
 
-    /**
-     * Retrieves the current task step that the template is in. For internal, host-side use only.
-     */
+    /** Retrieves the current task step that the template is in. */
     public int getCurrentTaskStep() {
         return mCurrentTaskStep;
     }
 
-    /**
-     * Sets the current task step that the template is in. For internal, host-side use only.
-     */
+    /** Sets the current task step that the template is in. */
     public void setCurrentTaskStep(int currentTaskStep) {
         this.mCurrentTaskStep = currentTaskStep;
     }
@@ -157,18 +154,12 @@
         return mIsRefresh;
     }
 
-    /**
-     * Updates the {@link Template} this {@link TemplateWrapper} instance wraps. For internal,
-     * host-side use only.
-     */
+    /** Updates the {@link Template} this {@link TemplateWrapper} instance wraps. */
     public void setTemplate(@NonNull Template template) {
         this.mTemplate = template;
     }
 
-    /**
-     * Updates the ID associated with the wrapped {@link Template}. For internal, host-side use
-     * only.
-     */
+    /** Updates the ID associated with the wrapped {@link Template}. */
     public void setId(@NonNull String id) {
         this.mId = id;
     }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Toggle.java b/car/app/app/src/main/java/androidx/car/app/model/Toggle.java
index 2da80a6..42a82a1 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Toggle.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Toggle.java
@@ -46,7 +46,7 @@
 
     /**
      * Returns the {@link OnCheckedChangeDelegate} that is called when the checked state of
-     * the {@link Toggle} is changed.
+     * the {@link Toggle} is changed or {@code null} if not set.
      */
     @Nullable
     public OnCheckedChangeDelegate getOnCheckedChangeDelegate() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
index 2a4d9f2..22be866 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
@@ -106,7 +106,7 @@
      * @throws IllegalArgumentException if the actions has more actions than allowed, if it has
      *                                  more actions with custom titles than allowed, if the
      *                                  actions do not contain all required types, or if the
-     *                                  actions contain any disallowed types.
+     *                                  actions contain any disallowed types
      */
     public void validateOrThrow(@NonNull List<Action> actions) {
         int maxAllowedActions = mMaxActions;
@@ -226,7 +226,7 @@
          * Returns a new builder that contains the same data as the given {@link ActionsConstraints}
          * instance.
          *
-         * @throws NullPointerException if {@code latLng} is {@code null}.
+         * @throws NullPointerException if {@code latLng} is {@code null}
          */
         public Builder(@NonNull ActionsConstraints constraints) {
             requireNonNull(constraints);
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java
index 258d8f3..ec6a2a9 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java
@@ -70,7 +70,7 @@
     /**
      * Returns {@code true} if the {@link CarColor} meets the constraints' requirement.
      *
-     * @throws IllegalArgumentException if the color type is not allowed.
+     * @throws IllegalArgumentException if the color type is not allowed
      */
     public void validateOrThrow(@NonNull CarColor carColor) {
         @CarColorType int type = carColor.getType();
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java
index dc504f8..6a7e754 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java
@@ -56,9 +56,8 @@
      * Returns {@code true} if the {@link CarIcon} meets the constraints' requirement.
      *
      * @throws IllegalStateException    if the custom icon does not have a backing
-     *                                  {@link IconCompat}
-     *                                  instance.
-     * @throws IllegalArgumentException if the custom icon type is not allowed.
+     *                                  {@link IconCompat} instance
+     * @throws IllegalArgumentException if the custom icon type is not allowed
      */
     public void validateOrThrow(@Nullable CarIcon carIcon) {
         if (carIcon == null || carIcon.getType() != CarIcon.TYPE_CUSTOM) {
@@ -76,7 +75,7 @@
     /**
      * Checks whether the given icon is supported.
      *
-     * @throws IllegalArgumentException if the given icon type is unsupported.
+     * @throws IllegalArgumentException if the given icon type is unsupported
      */
     @NonNull
     public IconCompat checkSupportedIcon(@NonNull IconCompat iconCompat) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
index 9c9fee9..e767e5b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
@@ -112,7 +112,7 @@
     /**
      * Validates that the given row satisfies this {@link RowConstraints} instance.
      *
-     * @throws IllegalArgumentException if the constraints are not met.
+     * @throws IllegalArgumentException if the constraints are not met
      */
     public void validateOrThrow(@NonNull Row row) {
         if (!mIsOnClickListenerAllowed && row.getOnClickDelegate() != null) {
@@ -215,7 +215,7 @@
          * Returns a new builder that contains the same data as the given {@link RowConstraints}
          * instance.
          *
-         * @throws NullPointerException if {@code latLng} is {@code null}.
+         * @throws NullPointerException if {@code latLng} is {@code null}
          */
         public Builder(@NonNull RowConstraints constraints) {
             requireNonNull(constraints);
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
index ae2d5c3..9f3d120 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
@@ -107,7 +107,7 @@
      * Validates that the {@link ItemList} satisfies this {@link RowListConstraints} instance.
      *
      * @throws IllegalArgumentException if the constraints are not met, or if the list contains
-     *                                  non-Row instances.
+     *                                  non-row instances
      */
     public void validateOrThrow(@NonNull ItemList itemList) {
         if (itemList.getOnSelectedDelegate() != null && !mAllowSelectableLists) {
@@ -122,7 +122,7 @@
      * {@link RowListConstraints} instance.
      *
      * @throws IllegalArgumentException if the constraints are not met or if the lists contain
-     *                                  any non-Row instances.
+     *                                  any non-row instances
      */
     public void validateOrThrow(@NonNull List<SectionedItemList> sections) {
         List<Item> combinedLists = new ArrayList<>();
@@ -142,7 +142,7 @@
     /**
      * Validates that the {@link Pane} satisfies this {@link RowListConstraints} instance.
      *
-     * @throws IllegalArgumentException if the constraints are not met.
+     * @throws IllegalArgumentException if the constraints are not met
      */
     public void validateOrThrow(@NonNull Pane pane) {
         List<Action> actions = pane.getActions();
@@ -214,7 +214,7 @@
         /**
          * Return a a new builder for the given {@link RowListConstraints} instance.
          *
-         * @throws NullPointerException if {@code latLng} is {@code null}.
+         * @throws NullPointerException if {@code latLng} is {@code null}
          */
         public Builder(@NonNull RowListConstraints constraints) {
             requireNonNull(constraints);
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java b/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java
index c5c7624..763335c 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java
@@ -94,7 +94,6 @@
      * displays the associated icon may be shown.
      *
      * @param trip destination, steps, and trip estimates to be sent to the host
-     *
      * @throws HostException            if the call is invoked by an app that is not declared as
      *                                  a navigation app in the manifest
      * @throws IllegalStateException    if the call occurs when navigation is not started (see
@@ -134,7 +133,6 @@
      * {@link #setNavigationManagerCallback(Executor, NavigationManagerCallback)}.
      *
      * @param callback the {@link NavigationManagerCallback} to use
-     *
      * @throws IllegalStateException if the current thread is not the main thread
      */
     @SuppressLint("ExecutorRegistration")
@@ -150,7 +148,6 @@
      *
      * @param executor the executor which will be used for invoking the callback
      * @param callback the {@link NavigationManagerCallback} to use
-     *
      * @throws IllegalStateException if the current thread is not the main thread
      */
     @MainThread
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java
index 6ea3566..88cea7c 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java
@@ -34,7 +34,7 @@
 import java.util.Objects;
 
 /** Information about a maneuver that the driver will be required to perform. */
-// TODO(rasekh): Update when host(s) updates or a scheme for auto sync is established.
+// TODO(b/154671667): Update when host(s) updates or a scheme for auto sync is established.
 public final class Maneuver {
     /**
      * Possible maneuver types.
@@ -72,8 +72,6 @@
             TYPE_MERGE_LEFT,
             TYPE_MERGE_RIGHT,
             TYPE_MERGE_SIDE_UNSPECIFIED,
-            TYPE_ROUNDABOUT_ENTER,
-            TYPE_ROUNDABOUT_EXIT,
             TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW,
             TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE,
             TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW,
@@ -293,30 +291,6 @@
     public static final int TYPE_MERGE_SIDE_UNSPECIFIED = 29;
 
     /**
-     * Roundabout entrance on which the current road ends.
-     *
-     * <p>For example, this is used to indicate "Enter the roundabout".
-     *
-     * @deprecated Use {@link #TYPE_ROUNDABOUT_ENTER_CW} or {@link #TYPE_ROUNDABOUT_ENTER_CCW}
-     * instead.
-     */
-    @Deprecated
-    @Type
-    public static final int TYPE_ROUNDABOUT_ENTER = 30;
-
-    /**
-     * Used when leaving a roundabout when the step starts in it.
-     *
-     * <p>For example, this is used to indicate "Exit the roundabout".
-     *
-     * @deprecated Use {@link #TYPE_ROUNDABOUT_EXIT_CW} or {@link #TYPE_ROUNDABOUT_EXIT_CCW}
-     * instead.
-     */
-    @Deprecated
-    @Type
-    public static final int TYPE_ROUNDABOUT_EXIT = 31;
-
-    /**
      * Enter a clockwise roundabout and take the Nth exit.
      *
      * <p>The exit number must be passed when created the maneuver.
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
index 729db82..0f431dd 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
@@ -33,8 +33,6 @@
 
 import java.util.Objects;
 
-// TODO(rampara): Update code reference to CarAppExtender is javadoc to link
-
 /**
  * A template for showing navigation information.
  *
@@ -51,7 +49,7 @@
  * <p>The template itself does not expose a drawing surface. In order to draw on the canvas, use
  * {@link androidx.car.app.AppManager#setSurfaceCallback(SurfaceCallback)}.
  *
- * <p>See {@code androidx.car.app.notification.CarAppExtender} for how to show
+ * <p>See {@link androidx.car.app.notification.CarAppExtender} for how to show
  * alerts with notifications. Frequent alert notifications distract the driver and are discouraged.
  *
  * <h4>Template Restrictions</h4>
@@ -89,6 +87,16 @@
     private final ActionStrip mActionStrip;
 
     /**
+     * Returns the {@link ActionStrip} for this template, or {@code null} if one isn't set.
+     *
+     * @see Builder#setActionStrip(ActionStrip)
+     */
+    @Nullable
+    public ActionStrip getActionStrip() {
+        return requireNonNull(mActionStrip);
+    }
+
+    /**
      * Returns the navigation information displayed on the template, or {@code null} if there is no
      * navigation information on top of the map.
      */
@@ -115,14 +123,6 @@
         return mDestinationTravelEstimate;
     }
 
-    /**
-     * Returns the {@link ActionStrip} with a list of the template-scoped actions for this template.
-     */
-    @Nullable
-    public ActionStrip getActionStrip() {
-        return mActionStrip;
-    }
-
     @NonNull
     @Override
     public String toString() {
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java
index 13879e1..fd7d6c1 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java
@@ -81,30 +81,57 @@
     @Nullable
     private final ActionStrip mActionStrip;
 
+    /**
+     * Returns the title of the template, or {@code null} if one is not set.
+     *
+     * @see Builder#setTitle(CharSequence)
+     */
     @Nullable
     public CarText getTitle() {
         return mTitle;
     }
 
-    public boolean isLoading() {
-        return mIsLoading;
-    }
-
-    @Nullable
-    public ItemList getItemList() {
-        return mItemList;
-    }
-
+    /**
+     * Returns the {@link Action} that is set to be displayed in the header of the template, or
+     * {@code null} if one is not set.
+     *
+     * @see Builder#setHeaderAction(Action)
+     */
     @Nullable
     public Action getHeaderAction() {
         return mHeaderAction;
     }
 
+    /**
+     * Returns the {@link ActionStrip} for this template, or {@code null} if one isn't set.
+     *
+     * @see Builder#setActionStrip(ActionStrip)
+     */
     @Nullable
     public ActionStrip getActionStrip() {
         return mActionStrip;
     }
 
+    /**
+     * Returns whether the template is loading.
+     *
+     * @see Builder#setLoading(boolean)
+     */
+    public boolean isLoading() {
+        return mIsLoading;
+    }
+
+    /**
+     * Returns the list of items to display alongside the map or {@code null} if the list is not
+     * set.
+     *
+     * @see Builder#setItemList(ItemList)
+     */
+    @Nullable
+    public ItemList getItemList() {
+        return mItemList;
+    }
+
     @NonNull
     @Override
     public String toString() {
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java
index b0a80be..b97dccd 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java
@@ -92,13 +92,41 @@
     private final ActionStrip mActionStrip;
 
     /**
-     * Returns the {@link CarText} that should be used as the title in the template.
+     * Returns the title of the template, or {@code null} if one is not set.
+     *
+     * @see Builder#setTitle(CharSequence)
      */
     @Nullable
     public CarText getTitle() {
         return mTitle;
     }
 
+    /**
+     * Returns the {@link Action} that is set to be displayed in the header of the template, or
+     * {@code null} if one is not set.
+     *
+     * @see Builder#setHeaderAction(Action)
+     */
+    @Nullable
+    public Action getHeaderAction() {
+        return mHeaderAction;
+    }
+
+    /**
+     * Returns the {@link ActionStrip} for this template, or {@code null} if one isn't set.
+     *
+     * @see Builder#setActionStrip(ActionStrip)
+     */
+    @Nullable
+    public ActionStrip getActionStrip() {
+        return mActionStrip;
+    }
+
+    /**
+     * Returns whether the template is loading.
+     *
+     * @see Builder#setLoading(boolean)
+     */
     public boolean isLoading() {
         return mIsLoading;
     }
@@ -113,16 +141,6 @@
         return mItemList;
     }
 
-    @Nullable
-    public Action getHeaderAction() {
-        return mHeaderAction;
-    }
-
-    @Nullable
-    public ActionStrip getActionStrip() {
-        return mActionStrip;
-    }
-
     @NonNull
     @Override
     public String toString() {
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
index 47dc3ae..ff929333 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
@@ -48,6 +48,15 @@
     @Keep
     private final boolean mIsLoading;
 
+    /**
+     * Returns whether the routing info is in a loading state.
+     *
+     * @see Builder#setLoading(boolean)
+     */
+    public boolean isLoading() {
+        return mIsLoading;
+    }
+
     @Nullable
     public Step getCurrentStep() {
         return mCurrentStep;
@@ -68,10 +77,6 @@
         return mJunctionImage;
     }
 
-    public boolean isLoading() {
-        return mIsLoading;
-    }
-
     @NonNull
     @Override
     public String toString() {
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
index 5c5f324..d200bb7 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
@@ -61,7 +61,6 @@
         return mRemainingDistance;
     }
 
-    // TODO(rampara): Returned time values must be in milliseconds
     @SuppressWarnings("MethodNameUnits")
     public long getRemainingTimeSeconds() {
         return mRemainingTimeSeconds >= 0 ? mRemainingTimeSeconds : REMAINING_TIME_UNKNOWN;
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
index 9c6f01e..369b6f7 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
@@ -50,6 +50,15 @@
     @Keep
     private final boolean mIsLoading;
 
+    /**
+     * Returns whether the trip is in a loading state.
+     *
+     * @see Builder#setLoading(boolean)
+     */
+    public boolean isLoading() {
+        return mIsLoading;
+    }
+
     @NonNull
     public List<Destination> getDestinations() {
         return CollectionUtils.emptyIfNull(mDestinations);
@@ -75,10 +84,6 @@
         return mCurrentRoad;
     }
 
-    public boolean isLoading() {
-        return mIsLoading;
-    }
-
     @Override
     @NonNull
     public String toString() {
diff --git a/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java b/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java
index 41aa2a73..63151da 100644
--- a/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java
+++ b/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java
@@ -523,7 +523,6 @@
          *               widget.
          * @throws NullPointerException if {@code title} or {@code intent} are {@code null}.
          */
-        // TODO(rampara): Update to remove use of deprecated Action API
         @SuppressWarnings("deprecation")
         @NonNull
         public Builder addAction(
diff --git a/car/app/app/src/main/java/androidx/car/app/serialization/Bundleable.java b/car/app/app/src/main/java/androidx/car/app/serialization/Bundleable.java
index c25dec2..01ab47a 100644
--- a/car/app/app/src/main/java/androidx/car/app/serialization/Bundleable.java
+++ b/car/app/app/src/main/java/androidx/car/app/serialization/Bundleable.java
@@ -28,7 +28,6 @@
  * A class that serializes and stores an object for sending over IPC.
  */
 @SuppressWarnings("BanParcelableUsage")
-// TODO(rampara): Investigate transition to VersionedParcelable.
 public final class Bundleable implements Parcelable {
     private final Bundle mBundle;
 
diff --git a/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java b/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java
index 969153e..54646dd 100644
--- a/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java
+++ b/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java
@@ -67,7 +67,6 @@
      */
     @SuppressLint("LambdaLast")
     @Nullable
-    // TODO(rampara): Change method signature to change parameter order.
     public static <ReturnT> ReturnT call(@NonNull RemoteCall<ReturnT> remoteCall,
             @NonNull String callName) {
         try {
@@ -150,7 +149,6 @@
      * <p>If the app throws an exception, will call {@link IOnDoneCallback#onFailure} with a {@link
      * FailureResponse} including information from the caught exception.
      */
-    // TODO(rampara): Change method signature to change parameter order.
     @SuppressLint("LambdaLast")
     public static void dispatchHostCall(
             @NonNull HostCall hostCall, @NonNull IOnDoneCallback callback,
diff --git a/car/app/app/src/test/java/androidx/car/app/CarAppServiceTest.java b/car/app/app/src/test/java/androidx/car/app/CarAppServiceTest.java
index abd1894..571a572 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarAppServiceTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarAppServiceTest.java
@@ -99,6 +99,8 @@
                     @NonNull
                     public Session onCreateSession() {
                         Session testSession = createTestSession();
+                        mCarContext = TestCarContext.createCarContext(
+                                ApplicationProvider.getApplicationContext());
                         CarAppServiceController.of(mCarContext, testSession, mCarAppService);
                         return testSession;
                     }
@@ -351,6 +353,8 @@
         HandshakeInfo handshakeInfo = new HandshakeInfo(hostPackageName, hostApiLevel);
         carApp.onHandshakeCompleted(Bundleable.create(handshakeInfo), mock(IOnDoneCallback.class));
         carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
+
+        currentSession = mCarAppService.getCurrentSession();
         assertThat(currentSession.getCarContext().getCarService(
                 ScreenManager.class).getScreenStack()).hasSize(1);
     }
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index 2dccb46..c7b04d2 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -75,14 +75,14 @@
   }
 
   public interface Animation<T, V extends androidx.compose.animation.core.AnimationVector> {
-    method public long getDurationMillis();
+    method public long getDurationNanos();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
-    method public T! getValue(long playTime);
-    method public V getVelocityVector(long playTime);
-    method public default boolean isFinished(long playTime);
+    method public T! getValueFromNanos(long playTimeNanos);
+    method public V getVelocityVectorFromNanos(long playTimeNanos);
+    method public default boolean isFinishedFromNanos(long playTimeNanos);
     method public boolean isInfinite();
-    property public abstract long durationMillis;
+    property public abstract long durationNanos;
     property public abstract boolean isInfinite;
     property public abstract T! targetValue;
     property public abstract androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
@@ -113,7 +113,7 @@
   public final class AnimationKt {
     method public static androidx.compose.animation.core.DecayAnimation<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> DecayAnimation(androidx.compose.animation.core.FloatDecayAnimationSpec animationSpec, float initialValue, optional float initialVelocity);
     method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.TargetBasedAnimation<T,V> TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, T? initialVelocity);
-    method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocity(androidx.compose.animation.core.Animation<T,V>, long playTime);
+    method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocityFromNanos(androidx.compose.animation.core.Animation<T,V>, long playTimeNanos);
   }
 
   public final class AnimationResult<T, V extends androidx.compose.animation.core.AnimationVector> {
@@ -289,15 +289,15 @@
     ctor public DecayAnimation(androidx.compose.animation.core.VectorizedDecayAnimationSpec<V> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
     ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
     ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? initialVelocity);
-    method public long getDurationMillis();
+    method public long getDurationNanos();
     method public T! getInitialValue();
     method public V getInitialVelocityVector();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
-    method public T! getValue(long playTime);
-    method public V getVelocityVector(long playTime);
+    method public T! getValueFromNanos(long playTimeNanos);
+    method public V getVelocityVectorFromNanos(long playTimeNanos);
     method public boolean isInfinite();
-    property public long durationMillis;
+    property public long durationNanos;
     property public final T! initialValue;
     property public final V initialVelocityVector;
     property public boolean isInfinite;
@@ -341,19 +341,19 @@
   }
 
   public interface FloatAnimationSpec extends androidx.compose.animation.core.AnimationSpec<java.lang.Float> {
-    method public long getDurationMillis(float start, float end, float startVelocity);
-    method public default float getEndVelocity(float start, float end, float startVelocity);
-    method public float getValue(long playTime, float start, float end, float startVelocity);
-    method public float getVelocity(long playTime, float start, float end, float startVelocity);
+    method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+    method public default float getEndVelocity(float initialValue, float targetValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
     method public default <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFloatAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<java.lang.Float,V> converter);
   }
 
   public interface FloatDecayAnimationSpec {
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(float start, float startVelocity);
-    method public float getTarget(float start, float startVelocity);
-    method public float getValue(long playTime, float start, float startVelocity);
-    method public float getVelocity(long playTime, float start, float startVelocity);
+    method public long getDurationNanos(float initialValue, float initialVelocity);
+    method public float getTargetValue(float initialValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
     property public abstract float absVelocityThreshold;
   }
 
@@ -364,10 +364,10 @@
     ctor public FloatExponentialDecaySpec(float frictionMultiplier, float absVelocityThreshold);
     ctor public FloatExponentialDecaySpec();
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(float start, float startVelocity);
-    method public float getTarget(float start, float startVelocity);
-    method public float getValue(long playTime, float start, float startVelocity);
-    method public float getVelocity(long playTime, float start, float startVelocity);
+    method public long getDurationNanos(float initialValue, float initialVelocity);
+    method public float getTargetValue(float initialValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
     property public float absVelocityThreshold;
   }
 
@@ -375,10 +375,10 @@
     ctor public FloatSpringSpec(float dampingRatio, float stiffness, float visibilityThreshold);
     ctor public FloatSpringSpec();
     method public float getDampingRatio();
-    method public long getDurationMillis(float start, float end, float startVelocity);
+    method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
     method public float getStiffness();
-    method public float getValue(long playTime, float start, float end, float startVelocity);
-    method public float getVelocity(long playTime, float start, float end, float startVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
     property public final float dampingRatio;
     property public final float stiffness;
   }
@@ -388,9 +388,9 @@
     ctor public FloatTweenSpec();
     method public int getDelay();
     method public int getDuration();
-    method public long getDurationMillis(float start, float end, float startVelocity);
-    method public float getValue(long playTime, float start, float end, float startVelocity);
-    method public float getVelocity(long playTime, float start, float end, float startVelocity);
+    method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
     property public final int delay;
     property public final int duration;
   }
@@ -558,14 +558,14 @@
 
   public final class TargetBasedAnimation<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.Animation<T,V> {
     ctor public TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, V? initialVelocityVector);
-    method public long getDurationMillis();
+    method public long getDurationNanos();
     method public T! getInitialValue();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
-    method public T! getValue(long playTime);
-    method public V getVelocityVector(long playTime);
+    method public T! getValueFromNanos(long playTimeNanos);
+    method public V getVelocityVectorFromNanos(long playTimeNanos);
     method public boolean isInfinite();
-    property public long durationMillis;
+    property public long durationNanos;
     property public final T! initialValue;
     property public boolean isInfinite;
     property public T! targetValue;
@@ -652,10 +652,10 @@
   }
 
   public interface VectorizedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
-    method public long getDurationMillis(V start, V end, V startVelocity);
-    method public default V getEndVelocity(V start, V end, V startVelocity);
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+    method public default V getEndVelocity(V initialValue, V targetValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     method public boolean isInfinite();
     property public abstract boolean isInfinite;
   }
@@ -665,17 +665,17 @@
 
   public interface VectorizedDecayAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(V initialValue, V initialVelocity);
-    method public V getTarget(V initialValue, V initialVelocity);
-    method public V getValue(long playTime, V initialValue, V initialVelocity);
-    method public V getVelocity(long playTime, V initialValue, V initialVelocity);
+    method public long getDurationNanos(V initialValue, V initialVelocity);
+    method public V getTargetValue(V initialValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
     property public abstract float absVelocityThreshold;
   }
 
   public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     method public int getDelayMillis();
     method public int getDurationMillis();
-    method public default long getDurationMillis(V start, V end, V startVelocity);
+    method public default long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
     property public abstract int delayMillis;
     property public abstract int durationMillis;
   }
@@ -687,9 +687,9 @@
 
   public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedFloatAnimationSpec(androidx.compose.animation.core.FloatAnimationSpec anim);
-    method public long getDurationMillis(V start, V end, V startVelocity);
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
   }
 
   public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
@@ -702,17 +702,17 @@
     ctor public VectorizedKeyframesSpec(java.util.Map<java.lang.Integer,? extends kotlin.Pair<? extends V,? extends androidx.compose.animation.core.Easing>> keyframes, int durationMillis, int delayMillis);
     method public int getDelayMillis();
     method public int getDurationMillis();
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     property public int delayMillis;
     property public int durationMillis;
   }
 
   public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedRepeatableSpec(int iterations, androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
-    method public long getDurationMillis(V start, V end, V startVelocity);
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
   }
 
   public final class VectorizedSnapSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
@@ -720,8 +720,8 @@
     ctor public VectorizedSnapSpec();
     method public int getDelayMillis();
     method public int getDurationMillis();
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     property public int delayMillis;
     property public int durationMillis;
   }
@@ -740,8 +740,8 @@
     method public int getDelayMillis();
     method public int getDurationMillis();
     method public androidx.compose.animation.core.Easing getEasing();
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     property public int delayMillis;
     property public int durationMillis;
     property public final androidx.compose.animation.core.Easing easing;
diff --git a/compose/animation/animation-core/api/public_plus_experimental_current.txt b/compose/animation/animation-core/api/public_plus_experimental_current.txt
index 2dccb46..c7b04d2 100644
--- a/compose/animation/animation-core/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation-core/api/public_plus_experimental_current.txt
@@ -75,14 +75,14 @@
   }
 
   public interface Animation<T, V extends androidx.compose.animation.core.AnimationVector> {
-    method public long getDurationMillis();
+    method public long getDurationNanos();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
-    method public T! getValue(long playTime);
-    method public V getVelocityVector(long playTime);
-    method public default boolean isFinished(long playTime);
+    method public T! getValueFromNanos(long playTimeNanos);
+    method public V getVelocityVectorFromNanos(long playTimeNanos);
+    method public default boolean isFinishedFromNanos(long playTimeNanos);
     method public boolean isInfinite();
-    property public abstract long durationMillis;
+    property public abstract long durationNanos;
     property public abstract boolean isInfinite;
     property public abstract T! targetValue;
     property public abstract androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
@@ -113,7 +113,7 @@
   public final class AnimationKt {
     method public static androidx.compose.animation.core.DecayAnimation<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> DecayAnimation(androidx.compose.animation.core.FloatDecayAnimationSpec animationSpec, float initialValue, optional float initialVelocity);
     method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.TargetBasedAnimation<T,V> TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, T? initialVelocity);
-    method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocity(androidx.compose.animation.core.Animation<T,V>, long playTime);
+    method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocityFromNanos(androidx.compose.animation.core.Animation<T,V>, long playTimeNanos);
   }
 
   public final class AnimationResult<T, V extends androidx.compose.animation.core.AnimationVector> {
@@ -289,15 +289,15 @@
     ctor public DecayAnimation(androidx.compose.animation.core.VectorizedDecayAnimationSpec<V> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
     ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
     ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? initialVelocity);
-    method public long getDurationMillis();
+    method public long getDurationNanos();
     method public T! getInitialValue();
     method public V getInitialVelocityVector();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
-    method public T! getValue(long playTime);
-    method public V getVelocityVector(long playTime);
+    method public T! getValueFromNanos(long playTimeNanos);
+    method public V getVelocityVectorFromNanos(long playTimeNanos);
     method public boolean isInfinite();
-    property public long durationMillis;
+    property public long durationNanos;
     property public final T! initialValue;
     property public final V initialVelocityVector;
     property public boolean isInfinite;
@@ -341,19 +341,19 @@
   }
 
   public interface FloatAnimationSpec extends androidx.compose.animation.core.AnimationSpec<java.lang.Float> {
-    method public long getDurationMillis(float start, float end, float startVelocity);
-    method public default float getEndVelocity(float start, float end, float startVelocity);
-    method public float getValue(long playTime, float start, float end, float startVelocity);
-    method public float getVelocity(long playTime, float start, float end, float startVelocity);
+    method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+    method public default float getEndVelocity(float initialValue, float targetValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
     method public default <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFloatAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<java.lang.Float,V> converter);
   }
 
   public interface FloatDecayAnimationSpec {
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(float start, float startVelocity);
-    method public float getTarget(float start, float startVelocity);
-    method public float getValue(long playTime, float start, float startVelocity);
-    method public float getVelocity(long playTime, float start, float startVelocity);
+    method public long getDurationNanos(float initialValue, float initialVelocity);
+    method public float getTargetValue(float initialValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
     property public abstract float absVelocityThreshold;
   }
 
@@ -364,10 +364,10 @@
     ctor public FloatExponentialDecaySpec(float frictionMultiplier, float absVelocityThreshold);
     ctor public FloatExponentialDecaySpec();
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(float start, float startVelocity);
-    method public float getTarget(float start, float startVelocity);
-    method public float getValue(long playTime, float start, float startVelocity);
-    method public float getVelocity(long playTime, float start, float startVelocity);
+    method public long getDurationNanos(float initialValue, float initialVelocity);
+    method public float getTargetValue(float initialValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
     property public float absVelocityThreshold;
   }
 
@@ -375,10 +375,10 @@
     ctor public FloatSpringSpec(float dampingRatio, float stiffness, float visibilityThreshold);
     ctor public FloatSpringSpec();
     method public float getDampingRatio();
-    method public long getDurationMillis(float start, float end, float startVelocity);
+    method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
     method public float getStiffness();
-    method public float getValue(long playTime, float start, float end, float startVelocity);
-    method public float getVelocity(long playTime, float start, float end, float startVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
     property public final float dampingRatio;
     property public final float stiffness;
   }
@@ -388,9 +388,9 @@
     ctor public FloatTweenSpec();
     method public int getDelay();
     method public int getDuration();
-    method public long getDurationMillis(float start, float end, float startVelocity);
-    method public float getValue(long playTime, float start, float end, float startVelocity);
-    method public float getVelocity(long playTime, float start, float end, float startVelocity);
+    method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
     property public final int delay;
     property public final int duration;
   }
@@ -558,14 +558,14 @@
 
   public final class TargetBasedAnimation<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.Animation<T,V> {
     ctor public TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, V? initialVelocityVector);
-    method public long getDurationMillis();
+    method public long getDurationNanos();
     method public T! getInitialValue();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
-    method public T! getValue(long playTime);
-    method public V getVelocityVector(long playTime);
+    method public T! getValueFromNanos(long playTimeNanos);
+    method public V getVelocityVectorFromNanos(long playTimeNanos);
     method public boolean isInfinite();
-    property public long durationMillis;
+    property public long durationNanos;
     property public final T! initialValue;
     property public boolean isInfinite;
     property public T! targetValue;
@@ -652,10 +652,10 @@
   }
 
   public interface VectorizedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
-    method public long getDurationMillis(V start, V end, V startVelocity);
-    method public default V getEndVelocity(V start, V end, V startVelocity);
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+    method public default V getEndVelocity(V initialValue, V targetValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     method public boolean isInfinite();
     property public abstract boolean isInfinite;
   }
@@ -665,17 +665,17 @@
 
   public interface VectorizedDecayAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(V initialValue, V initialVelocity);
-    method public V getTarget(V initialValue, V initialVelocity);
-    method public V getValue(long playTime, V initialValue, V initialVelocity);
-    method public V getVelocity(long playTime, V initialValue, V initialVelocity);
+    method public long getDurationNanos(V initialValue, V initialVelocity);
+    method public V getTargetValue(V initialValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
     property public abstract float absVelocityThreshold;
   }
 
   public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     method public int getDelayMillis();
     method public int getDurationMillis();
-    method public default long getDurationMillis(V start, V end, V startVelocity);
+    method public default long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
     property public abstract int delayMillis;
     property public abstract int durationMillis;
   }
@@ -687,9 +687,9 @@
 
   public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedFloatAnimationSpec(androidx.compose.animation.core.FloatAnimationSpec anim);
-    method public long getDurationMillis(V start, V end, V startVelocity);
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
   }
 
   public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
@@ -702,17 +702,17 @@
     ctor public VectorizedKeyframesSpec(java.util.Map<java.lang.Integer,? extends kotlin.Pair<? extends V,? extends androidx.compose.animation.core.Easing>> keyframes, int durationMillis, int delayMillis);
     method public int getDelayMillis();
     method public int getDurationMillis();
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     property public int delayMillis;
     property public int durationMillis;
   }
 
   public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedRepeatableSpec(int iterations, androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
-    method public long getDurationMillis(V start, V end, V startVelocity);
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
   }
 
   public final class VectorizedSnapSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
@@ -720,8 +720,8 @@
     ctor public VectorizedSnapSpec();
     method public int getDelayMillis();
     method public int getDurationMillis();
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     property public int delayMillis;
     property public int durationMillis;
   }
@@ -740,8 +740,8 @@
     method public int getDelayMillis();
     method public int getDurationMillis();
     method public androidx.compose.animation.core.Easing getEasing();
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     property public int delayMillis;
     property public int durationMillis;
     property public final 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 9cf625e..1333ce8 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -75,14 +75,14 @@
   }
 
   public interface Animation<T, V extends androidx.compose.animation.core.AnimationVector> {
-    method public long getDurationMillis();
+    method public long getDurationNanos();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
-    method public T! getValue(long playTime);
-    method public V getVelocityVector(long playTime);
-    method public default boolean isFinished(long playTime);
+    method public T! getValueFromNanos(long playTimeNanos);
+    method public V getVelocityVectorFromNanos(long playTimeNanos);
+    method public default boolean isFinishedFromNanos(long playTimeNanos);
     method public boolean isInfinite();
-    property public abstract long durationMillis;
+    property public abstract long durationNanos;
     property public abstract boolean isInfinite;
     property public abstract T! targetValue;
     property public abstract androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
@@ -113,7 +113,7 @@
   public final class AnimationKt {
     method public static androidx.compose.animation.core.DecayAnimation<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> DecayAnimation(androidx.compose.animation.core.FloatDecayAnimationSpec animationSpec, float initialValue, optional float initialVelocity);
     method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.TargetBasedAnimation<T,V> TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, T? initialVelocity);
-    method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocity(androidx.compose.animation.core.Animation<T,V>, long playTime);
+    method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocityFromNanos(androidx.compose.animation.core.Animation<T,V>, long playTimeNanos);
   }
 
   public final class AnimationResult<T, V extends androidx.compose.animation.core.AnimationVector> {
@@ -289,15 +289,15 @@
     ctor public DecayAnimation(androidx.compose.animation.core.VectorizedDecayAnimationSpec<V> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
     ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
     ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? initialVelocity);
-    method public long getDurationMillis();
+    method public long getDurationNanos();
     method public T! getInitialValue();
     method public V getInitialVelocityVector();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
-    method public T! getValue(long playTime);
-    method public V getVelocityVector(long playTime);
+    method public T! getValueFromNanos(long playTimeNanos);
+    method public V getVelocityVectorFromNanos(long playTimeNanos);
     method public boolean isInfinite();
-    property public long durationMillis;
+    property public long durationNanos;
     property public final T! initialValue;
     property public final V initialVelocityVector;
     property public boolean isInfinite;
@@ -341,19 +341,19 @@
   }
 
   public interface FloatAnimationSpec extends androidx.compose.animation.core.AnimationSpec<java.lang.Float> {
-    method public long getDurationMillis(float start, float end, float startVelocity);
-    method public default float getEndVelocity(float start, float end, float startVelocity);
-    method public float getValue(long playTime, float start, float end, float startVelocity);
-    method public float getVelocity(long playTime, float start, float end, float startVelocity);
+    method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+    method public default float getEndVelocity(float initialValue, float targetValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
     method public default <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFloatAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<java.lang.Float,V> converter);
   }
 
   public interface FloatDecayAnimationSpec {
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(float start, float startVelocity);
-    method public float getTarget(float start, float startVelocity);
-    method public float getValue(long playTime, float start, float startVelocity);
-    method public float getVelocity(long playTime, float start, float startVelocity);
+    method public long getDurationNanos(float initialValue, float initialVelocity);
+    method public float getTargetValue(float initialValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
     property public abstract float absVelocityThreshold;
   }
 
@@ -364,10 +364,10 @@
     ctor public FloatExponentialDecaySpec(float frictionMultiplier, float absVelocityThreshold);
     ctor public FloatExponentialDecaySpec();
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(float start, float startVelocity);
-    method public float getTarget(float start, float startVelocity);
-    method public float getValue(long playTime, float start, float startVelocity);
-    method public float getVelocity(long playTime, float start, float startVelocity);
+    method public long getDurationNanos(float initialValue, float initialVelocity);
+    method public float getTargetValue(float initialValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
     property public float absVelocityThreshold;
   }
 
@@ -375,10 +375,10 @@
     ctor public FloatSpringSpec(float dampingRatio, float stiffness, float visibilityThreshold);
     ctor public FloatSpringSpec();
     method public float getDampingRatio();
-    method public long getDurationMillis(float start, float end, float startVelocity);
+    method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
     method public float getStiffness();
-    method public float getValue(long playTime, float start, float end, float startVelocity);
-    method public float getVelocity(long playTime, float start, float end, float startVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
     property public final float dampingRatio;
     property public final float stiffness;
   }
@@ -388,9 +388,9 @@
     ctor public FloatTweenSpec();
     method public int getDelay();
     method public int getDuration();
-    method public long getDurationMillis(float start, float end, float startVelocity);
-    method public float getValue(long playTime, float start, float end, float startVelocity);
-    method public float getVelocity(long playTime, float start, float end, float startVelocity);
+    method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
     property public final int delay;
     property public final int duration;
   }
@@ -558,14 +558,14 @@
 
   public final class TargetBasedAnimation<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.Animation<T,V> {
     ctor public TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, V? initialVelocityVector);
-    method public long getDurationMillis();
+    method public long getDurationNanos();
     method public T! getInitialValue();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
-    method public T! getValue(long playTime);
-    method public V getVelocityVector(long playTime);
+    method public T! getValueFromNanos(long playTimeNanos);
+    method public V getVelocityVectorFromNanos(long playTimeNanos);
     method public boolean isInfinite();
-    property public long durationMillis;
+    property public long durationNanos;
     property public final T! initialValue;
     property public boolean isInfinite;
     property public T! targetValue;
@@ -660,10 +660,10 @@
   }
 
   public interface VectorizedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
-    method public long getDurationMillis(V start, V end, V startVelocity);
-    method public default V getEndVelocity(V start, V end, V startVelocity);
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+    method public default V getEndVelocity(V initialValue, V targetValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     method public boolean isInfinite();
     property public abstract boolean isInfinite;
   }
@@ -673,17 +673,17 @@
 
   public interface VectorizedDecayAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(V initialValue, V initialVelocity);
-    method public V getTarget(V initialValue, V initialVelocity);
-    method public V getValue(long playTime, V initialValue, V initialVelocity);
-    method public V getVelocity(long playTime, V initialValue, V initialVelocity);
+    method public long getDurationNanos(V initialValue, V initialVelocity);
+    method public V getTargetValue(V initialValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
     property public abstract float absVelocityThreshold;
   }
 
   public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     method public int getDelayMillis();
     method public int getDurationMillis();
-    method public default long getDurationMillis(V start, V end, V startVelocity);
+    method public default long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
     property public abstract int delayMillis;
     property public abstract int durationMillis;
   }
@@ -695,9 +695,9 @@
 
   public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedFloatAnimationSpec(androidx.compose.animation.core.FloatAnimationSpec anim);
-    method public long getDurationMillis(V start, V end, V startVelocity);
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
   }
 
   public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
@@ -710,17 +710,17 @@
     ctor public VectorizedKeyframesSpec(java.util.Map<java.lang.Integer,? extends kotlin.Pair<? extends V,? extends androidx.compose.animation.core.Easing>> keyframes, int durationMillis, int delayMillis);
     method public int getDelayMillis();
     method public int getDurationMillis();
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     property public int delayMillis;
     property public int durationMillis;
   }
 
   public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
     ctor public VectorizedRepeatableSpec(int iterations, androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
-    method public long getDurationMillis(V start, V end, V startVelocity);
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
   }
 
   public final class VectorizedSnapSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
@@ -728,8 +728,8 @@
     ctor public VectorizedSnapSpec();
     method public int getDelayMillis();
     method public int getDurationMillis();
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     property public int delayMillis;
     property public int durationMillis;
   }
@@ -748,8 +748,8 @@
     method public int getDelayMillis();
     method public int getDurationMillis();
     method public androidx.compose.animation.core.Easing getEasing();
-    method public V getValue(long playTime, V start, V end, V startVelocity);
-    method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+    method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
     property public int delayMillis;
     property public int durationMillis;
     property public final androidx.compose.animation.core.Easing easing;
diff --git a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
index 0ce2950..766b3d2 100644
--- a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
+++ b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
@@ -87,13 +87,15 @@
                     val startTime = withFrameNanos { it }
                     var playTime = 0L
                     while (playTime < 2100L) {
-                        playTime = (withFrameNanos { it } - startTime) / 1_000_000L
-                        var iterationTime = playTime % 2000
-                        if (iterationTime > 1000L) {
-                            iterationTime = 2000L - iterationTime
+                        playTime = withFrameNanos { it } - startTime
+                        var iterationTime = playTime % (2000 * MillisToNanos)
+                        if (iterationTime > 1000 * MillisToNanos) {
+                            iterationTime = 2000L * MillisToNanos - iterationTime
                         }
-                        val expectedFloat = keyframesAnim.getValue(iterationTime)
-                        val expectedColor = colorAnim.getValue(playTime % 1000)
+                        val expectedFloat = keyframesAnim.getValueFromNanos(iterationTime)
+                        val expectedColor = colorAnim.getValueFromNanos(
+                            playTime % (1000 * MillisToNanos)
+                        )
                         assertEquals(expectedFloat, animFloat.value, 0.01f)
                         assertEquals(expectedColor, animColor.value)
                     }
diff --git a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
index fe66c47..b8acd42 100644
--- a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
+++ b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
@@ -350,28 +350,31 @@
 
                 val floatValue by animateFloatAsState(if (enabled) 100f else 0f)
 
-                val durationForFloat = specForFloat.getDurationMillis(0f, 100f, 0f)
-                val durationForOffset = specForOffset.getDurationMillis(0f, 100f, 0f)
-                val durationForBounds = specForBounds.getDurationMillis(0f, 100f, 0f)
+                val durationForFloat = specForFloat.getDurationNanos(0f, 100f, 0f)
+                val durationForOffset = specForOffset.getDurationNanos(0f, 100f, 0f)
+                val durationForBounds = specForBounds.getDurationNanos(0f, 100f, 0f)
 
                 if (enabled) {
                     LaunchedEffect(Unit) {
                         val startTime = withFrameNanos { it }
                         var frameTime = startTime
                         do {
-                            val playTime = (frameTime - startTime) / 1_000_000L
-                            val expectFloat = specForFloat.getValue(playTime, 0f, 100f, 0f)
+                            val playTime = frameTime - startTime
+                            val expectFloat =
+                                specForFloat.getValueFromNanos(playTime, 0f, 100f, 0f)
                             assertEquals("play time: $playTime", expectFloat, floatValue)
 
                             if (playTime < durationForOffset) {
-                                val expectOffset = specForOffset.getValue(playTime, 0f, 100f, 0f)
+                                val expectOffset =
+                                    specForOffset.getValueFromNanos(playTime, 0f, 100f, 0f)
                                 assertEquals(Offset(expectOffset, expectOffset), offsetValue)
                             } else {
                                 assertEquals(Offset(100f, 100f), offsetValue)
                             }
 
                             if (playTime < durationForBounds) {
-                                val expectBounds = specForBounds.getValue(playTime, 0f, 100f, 0f)
+                                val expectBounds =
+                                    specForBounds.getValueFromNanos(playTime, 0f, 100f, 0f)
                                 assertEquals(
                                     Bounds(
                                         expectBounds.dp,
diff --git a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
index 47bccf1..157183c9 100644
--- a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
+++ b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
@@ -152,15 +152,15 @@
             if (transition.isRunning) {
                 if (transition.targetState == AnimStates.To) {
                     assertEquals(
-                        floatAnim1.getValue(transition.playTimeNanos / 1_000_000L),
+                        floatAnim1.getValueFromNanos(transition.playTimeNanos),
                         animFloat.value, 0.00001f
                     )
                     assertEquals(
-                        colorAnim1.getValue(transition.playTimeNanos / 1_000_000L),
+                        colorAnim1.getValueFromNanos(transition.playTimeNanos),
                         animColor.value
                     )
                     assertEquals(
-                        keyframesAnim1.getValue(transition.playTimeNanos / 1_000_000L),
+                        keyframesAnim1.getValueFromNanos(transition.playTimeNanos),
                         animFloatWithKeyframes.value, 0.00001f
                     )
 
@@ -168,15 +168,15 @@
                     assertEquals(AnimStates.From, transition.segment.initialState)
                 } else {
                     assertEquals(
-                        floatAnim2.getValue(transition.playTimeNanos / 1_000_000L),
+                        floatAnim2.getValueFromNanos(transition.playTimeNanos),
                         animFloat.value, 0.00001f
                     )
                     assertEquals(
-                        colorAnim2.getValue(transition.playTimeNanos / 1_000_000L),
+                        colorAnim2.getValueFromNanos(transition.playTimeNanos),
                         animColor.value
                     )
                     assertEquals(
-                        keyframesAnim2.getValue(transition.playTimeNanos / 1_000_000L),
+                        keyframesAnim2.getValueFromNanos(transition.playTimeNanos),
                         animFloatWithKeyframes.value, 0.00001f
                     )
                     assertEquals(AnimStates.From, transition.segment.targetState)
@@ -225,9 +225,9 @@
 
                     assertEquals(0f, actual.value)
                     do {
-                        playTime = (withFrameNanos { it } - startTime) / 1_000_000L
-                        assertEquals(anim.getValue(playTime), actual.value)
-                    } while (playTime <= 200)
+                        playTime = withFrameNanos { it } - startTime
+                        assertEquals(anim.getValueFromNanos(playTime), actual.value)
+                    } while (playTime <= 200 * MillisToNanos)
                 }
             }
         }
@@ -236,7 +236,7 @@
             target.value = AnimStates.To
         }
         rule.waitForIdle()
-        assertTrue(playTime > 200)
+        assertTrue(playTime > 200 * MillisToNanos)
     }
 
     @Test
@@ -260,8 +260,8 @@
                     if (it == AnimStates.From) 0f else 1000f
                 }
                 val anim = TargetBasedAnimation(tween(800), Float.VectorConverter, 0f, 1000f)
-                playTime = (transition.playTimeNanos - startTime) / 1_000_000L
-                assertEquals(anim.getValue(playTime), laterAdded.value)
+                playTime = transition.playTimeNanos - startTime
+                assertEquals(anim.getValueFromNanos(playTime), laterAdded.value)
             }
         }
 
@@ -269,7 +269,7 @@
             target.value = AnimStates.To
         }
         rule.waitForIdle()
-        assertTrue(playTime > 800)
+        assertTrue(playTime > 800 * MillisToNanos)
     }
 
     @Test
@@ -289,14 +289,14 @@
             LaunchedEffect(transition) {
                 val startTime = withFrameNanos { it }
                 val anim = TargetBasedAnimation(tween(800), Float.VectorConverter, 0f, 1000f)
-                while (!anim.isFinished(playTime)) {
-                    playTime = (withFrameNanos { it } - startTime) / 1_000_000L
-                    assertEquals(anim.getValue(playTime), floatAnim?.value)
+                while (!anim.isFinishedFromNanos(playTime)) {
+                    playTime = withFrameNanos { it } - startTime
+                    assertEquals(anim.getValueFromNanos(playTime), floatAnim?.value)
                 }
             }
         }
         rule.waitForIdle()
-        assertTrue(playTime >= 800)
+        assertTrue(playTime >= 800 * MillisToNanos)
         assertEquals(1000f, floatAnim?.value)
     }
 
@@ -327,9 +327,9 @@
 
                     val startTime = withFrameNanos { it }
                     val anim = TargetBasedAnimation(tween(800), Float.VectorConverter, 0f, 1000f)
-                    while (!anim.isFinished(playTime)) {
-                        playTime = (withFrameNanos { it } - startTime) / 1_000_000L
-                        assertEquals(anim.getValue(playTime), floatAnim.value)
+                    while (!anim.isFinishedFromNanos(playTime)) {
+                        playTime = withFrameNanos { it } - startTime
+                        assertEquals(anim.getValueFromNanos(playTime), floatAnim.value)
                     }
                 }
             }
@@ -337,6 +337,6 @@
 
         rule.waitForIdle()
         assertTrue(targetRecreated)
-        assertTrue(playTime >= 800)
+        assertTrue(playTime >= 800 * MillisToNanos)
     }
 }
\ No newline at end of file
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
index 30775bb..75d11c7 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
@@ -105,6 +105,7 @@
     internal var onEnd: ((AnimationEndReason, T) -> Unit)? = null
     private lateinit var anim: Animation<T, V>
     private var startTime: Long = Unset
+
     // last frame time only gets updated during the animation pulse. It will be reset at the
     // end of the animation.
     private var lastFrameTime: Long = Unset
@@ -188,21 +189,21 @@
         }
 
         lastFrameTime = timeMillis
-        velocityVector = anim.getVelocityVector(playtime)
-        value = anim.getValue(playtime)
+        velocityVector = anim.getVelocityVectorFromNanos(playtime * MillisToNanos)
+        value = anim.getValueFromNanos(playtime * MillisToNanos)
 
         checkFinished(playtime)
     }
 
     protected open fun checkFinished(playtime: Long) {
-        val animationFinished = anim.isFinished(playtime)
+        val animationFinished = anim.isFinishedFromNanos(playtime * MillisToNanos)
         if (animationFinished) endAnimation()
     }
 
     internal fun startAnimation(anim: Animation<T, V>) {
         this.anim = anim
         // Quick check before officially starting
-        if (anim.isFinished(0)) {
+        if (anim.isFinishedFromNanos(0)) {
             // If the animation value & velocity is already meeting the finished condition before
             // the animation even starts, end it now.
             endAnimation()
@@ -275,6 +276,7 @@
      */
     var min: Float = Float.NEGATIVE_INFINITY
         private set
+
     /**
      * Upper bound of the animation value. When animations reach this upper bound, it will
      * automatically stop with [AnimationEndReason] being [AnimationEndReason.BoundReached].
@@ -367,7 +369,7 @@
     }
 
     // start from current value with the given velocity
-    targetValue = decay.getTarget(value, startVelocity)
+    targetValue = decay.getTargetValue(value, startVelocity)
     startAnimation(DecayAnimation(decay, value, startVelocity))
 }
 
@@ -402,7 +404,7 @@
     }
 
     // start from current value with the given velocity
-    targetValue = decay.getTarget(value, startVelocity)
+    targetValue = decay.getTargetValue(value, startVelocity)
     val targetAnimation = adjustTarget(targetValue)
     if (targetAnimation == null) {
         val animWrapper = decay.createAnimation(value, startVelocity)
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 94ff29b..a9157b88 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
@@ -27,18 +27,19 @@
  *
  * __Note__: [Animation] does not track the lifecycle of an animation. It merely reacts to play time
  * change and returns the new value/velocity as a result. It can be used as a building block for
- * more lifecycle aware animations. In contrast, [Animatable] and [TransitionAnimation] are
+ * more lifecycle aware animations. In contrast, [Animatable] and [Transition] are
  * stateful and manage their own lifecycles, and subscribe/unsubscribe from an
  * [AnimationClockObservable] as needed.
  *
  * @see [Animatable]
- * @see [androidx.compose.animation.transition]
+ * @see [updateTransition]
  */
 interface Animation<T, V : AnimationVector> {
     /**
-     * This amount of time in milliseconds that the animation will run before it finishes
+     * This amount of time in nanoseconds that the animation will run before it finishes
      */
-    val durationMillis: Long
+    @get:Suppress("MethodNameUnits")
+    val durationNanos: Long
 
     /**
      * The [TwoWayConverter] that will be used to convert value/velocity from any arbitrary data
@@ -62,35 +63,39 @@
     /**
      * Returns the value of the animation at the given play time.
      *
-     * @param playTime the play time that is used to determine the value of the animation.
+     * @param playTimeNanos the play time that is used to determine the value of the animation.
      */
-    fun getValue(playTime: Long): T
+    fun getValueFromNanos(playTimeNanos: Long): T
 
     /**
      * Returns the velocity (in [AnimationVector] form) of the animation at the given play time.
      *
-     * @param playTime the play time that is used to calculate the velocity of the animation.
+     * @param playTimeNanos the play time that is used to calculate the velocity of the animation.
      */
-    fun getVelocityVector(playTime: Long): V
+    fun getVelocityVectorFromNanos(playTimeNanos: Long): V
 
     /**
      * Returns whether the animation is finished at the given play time.
      *
-     * @param playTime the play time used to determine whether the animation is finished.
+     * @param playTimeNanos the play time used to determine whether the animation is finished.
      */
-    fun isFinished(playTime: Long): Boolean {
-        return playTime >= durationMillis
+    fun isFinishedFromNanos(playTimeNanos: Long): Boolean {
+        return playTimeNanos >= durationNanos
     }
 }
 
+internal val Animation<*, *>.durationMillis: Long
+    get() = durationNanos / MillisToNanos
+
+internal const val MillisToNanos: Long = 1_000_000L
+
 /**
  * Returns the velocity of the animation at the given play time.
  *
- * @param playTime the play time that is used to calculate the velocity of the animation.
+ * @param playTimeNanos the play time that is used to calculate the velocity of the animation.
  */
-fun <T, V : AnimationVector> Animation<T, V>.getVelocity(playTime: Long): T =
-    typeConverter.convertFromVector(getVelocityVector(playTime))
-
+fun <T, V : AnimationVector> Animation<T, V>.getVelocityFromNanos(playTimeNanos: Long): T =
+    typeConverter.convertFromVector(getVelocityVectorFromNanos(playTimeNanos))
 /**
  * Creates a [TargetBasedAnimation] from a given [VectorizedAnimationSpec] of [AnimationVector] type. This
  * convenient method is intended for when the value being animated (i.e. start value, end value,
@@ -127,7 +132,7 @@
  * __Note__: When interruptions happen to the [TargetBasedAnimation], a new instance should
  * be created that use the current value and velocity as the starting conditions. This type of
  * interruption handling is the default behavior for both [Animatable] and
- * [TransitionAnimation]. Consider using those APIs for the interruption handling, as well as
+ * [Transition]. Consider using those APIs for the interruption handling, as well as
  * built-in animation lifecycle management.
  *
  * @param animationSpec the [AnimationSpec] that will be used to calculate value/velocity
@@ -161,7 +166,7 @@
  * __Note__: When interruptions happen to the [TargetBasedAnimation], a new instance should
  * be created that use the current value and velocity as the starting conditions. This type of
  * interruption handling is the default behavior for both [Animatable] and
- * [TransitionAnimation]. Consider using those APIs for the interruption handling, as well as
+ * [Transition]. Consider using those APIs for the interruption handling, as well as
  * built-in animation lifecycle management.
  *
  * @param animationSpec the [VectorizedAnimationSpec] that will be used to calculate value/velocity
@@ -170,8 +175,8 @@
  * @param typeConverter the [TwoWayConverter] that is used to convert animation type [T] from/to [V]
  * @param initialVelocityVector the start velocity of the animation in the form of [AnimationVector]
  *
- * @see [TransitionAnimation]
- * @see [androidx.compose.animation.transition]
+ * @see [Transition]
+ * @see [updateTransition]
  * @see [Animatable]
  */
 class TargetBasedAnimation<T, V : AnimationVector> internal constructor(
@@ -194,7 +199,7 @@
      * __Note__: When interruptions happen to the [TargetBasedAnimation], a new instance should
      * be created that use the current value and velocity as the starting conditions. This type of
      * interruption handling is the default behavior for both [Animatable] and
-     * [TransitionAnimation]. Consider using those APIs for the interruption handling, as well as
+     * [Transition]. Consider using those APIs for the interruption handling, as well as
      * built-in animation lifecycle management.
      *
      * @param animationSpec the [AnimationSpec] that will be used to calculate value/velocity
@@ -224,12 +229,11 @@
             .newInstance()
 
     override val isInfinite: Boolean get() = animationSpec.isInfinite
-
-    override fun getValue(playTime: Long): T {
-        return if (playTime < durationMillis) {
+    override fun getValueFromNanos(playTimeNanos: Long): T {
+        return if (!isFinishedFromNanos(playTimeNanos)) {
             typeConverter.convertFromVector(
-                animationSpec.getValue(
-                    playTime, initialValueVector,
+                animationSpec.getValueFromNanos(
+                    playTimeNanos, initialValueVector,
                     targetValueVector, initialVelocityVector
                 )
             )
@@ -238,10 +242,11 @@
         }
     }
 
-    override val durationMillis: Long = animationSpec.getDurationMillis(
-        start = initialValueVector,
-        end = targetValueVector,
-        startVelocity = this.initialVelocityVector
+    @get:Suppress("MethodNameUnits")
+    override val durationNanos: Long = animationSpec.getDurationNanos(
+        initialValue = initialValueVector,
+        targetValue = targetValueVector,
+        initialVelocity = this.initialVelocityVector
     )
 
     private val endVelocity = animationSpec.getEndVelocity(
@@ -250,10 +255,10 @@
         this.initialVelocityVector
     )
 
-    override fun getVelocityVector(playTime: Long): V {
-        return if (playTime < durationMillis) {
-            animationSpec.getVelocity(
-                playTime,
+    override fun getVelocityVectorFromNanos(playTimeNanos: Long): V {
+        return if (!isFinishedFromNanos(playTimeNanos)) {
+            animationSpec.getVelocityFromNanos(
+                playTimeNanos,
                 initialValueVector,
                 targetValueVector,
                 initialVelocityVector
@@ -289,9 +294,10 @@
     private val endVelocity: V
 
     override val targetValue: T = typeConverter.convertFromVector(
-        animationSpec.getTarget(initialValueVector, initialVelocityVector)
+        animationSpec.getTargetValue(initialValueVector, initialVelocityVector)
     )
-    override val durationMillis: Long
+    @get:Suppress("MethodNameUnits")
+    override val durationNanos: Long
 
     // DecayAnimation finishes by design
     override val isInfinite: Boolean = false
@@ -359,11 +365,11 @@
     )
 
     init {
-        durationMillis = animationSpec.getDurationMillis(
+        durationNanos = animationSpec.getDurationNanos(
             initialValueVector, initialVelocityVector
         )
-        endVelocity = animationSpec.getVelocity(
-            durationMillis,
+        endVelocity = animationSpec.getVelocityFromNanos(
+            durationNanos,
             initialValueVector,
             initialVelocityVector
         ).copy()
@@ -375,19 +381,27 @@
         }
     }
 
-    override fun getValue(playTime: Long): T {
-        if (!isFinished(playTime)) {
+    override fun getValueFromNanos(playTimeNanos: Long): T {
+        if (!isFinishedFromNanos(playTimeNanos)) {
             return typeConverter.convertFromVector(
-                animationSpec.getValue(playTime, initialValueVector, initialVelocityVector)
+                animationSpec.getValueFromNanos(
+                    playTimeNanos,
+                    initialValueVector,
+                    initialVelocityVector
+                )
             )
         } else {
             return targetValue
         }
     }
 
-    override fun getVelocityVector(playTime: Long): V {
-        if (!isFinished(playTime)) {
-            return animationSpec.getVelocity(playTime, initialValueVector, initialVelocityVector)
+    override fun getVelocityVectorFromNanos(playTimeNanos: Long): V {
+        if (!isFinishedFromNanos(playTimeNanos)) {
+            return animationSpec.getVelocityFromNanos(
+                playTimeNanos,
+                initialValueVector,
+                initialVelocityVector
+            )
         } else {
             return endVelocity
         }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
index 3bee21f..959c2d5 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
@@ -60,7 +60,7 @@
     initialVelocity: T
 ): T {
     val vectorizedSpec = vectorize(typeConverter)
-    val targetVector = vectorizedSpec.getTarget(
+    val targetVector = vectorizedSpec.getTargetValue(
         typeConverter.convertToVector(initialValue),
         typeConverter.convertToVector(initialVelocity)
     )
@@ -78,7 +78,7 @@
     initialVelocity: Float
 ): Float {
     val vectorizedSpec = vectorize(Float.VectorConverter)
-    val targetVector = vectorizedSpec.getTarget(
+    val targetVector = vectorizedSpec.getTargetValue(
         AnimationVector(initialValue),
         AnimationVector(initialVelocity)
     )
@@ -136,17 +136,21 @@
     private lateinit var targetVector: V
     override val absVelocityThreshold: Float = floatDecaySpec.absVelocityThreshold
 
-    override fun getValue(playTime: Long, initialValue: V, initialVelocity: V): V {
+    override fun getValueFromNanos(playTimeNanos: Long, initialValue: V, initialVelocity: V): V {
         if (!::valueVector.isInitialized) {
             valueVector = initialValue.newInstance()
         }
         for (i in 0 until valueVector.size) {
-            valueVector[i] = floatDecaySpec.getValue(playTime, initialValue[i], initialVelocity[i])
+            valueVector[i] = floatDecaySpec.getValueFromNanos(
+                playTimeNanos,
+                initialValue[i],
+                initialVelocity[i]
+            )
         }
         return valueVector
     }
 
-    override fun getDurationMillis(initialValue: V, initialVelocity: V): Long {
+    override fun getDurationNanos(initialValue: V, initialVelocity: V): Long {
         var maxDuration = 0L
         if (!::velocityVector.isInitialized) {
             velocityVector = initialValue.newInstance()
@@ -154,19 +158,19 @@
         for (i in 0 until velocityVector.size) {
             maxDuration = maxOf(
                 maxDuration,
-                floatDecaySpec.getDurationMillis(initialValue[i], initialVelocity[i])
+                floatDecaySpec.getDurationNanos(initialValue[i], initialVelocity[i])
             )
         }
         return maxDuration
     }
 
-    override fun getVelocity(playTime: Long, initialValue: V, initialVelocity: V): V {
+    override fun getVelocityFromNanos(playTimeNanos: Long, initialValue: V, initialVelocity: V): V {
         if (!::velocityVector.isInitialized) {
             velocityVector = initialValue.newInstance()
         }
         for (i in 0 until velocityVector.size) {
-            velocityVector[i] = floatDecaySpec.getVelocity(
-                playTime,
+            velocityVector[i] = floatDecaySpec.getVelocityFromNanos(
+                playTimeNanos,
                 initialValue[i],
                 initialVelocity[i]
             )
@@ -174,12 +178,12 @@
         return velocityVector
     }
 
-    override fun getTarget(initialValue: V, initialVelocity: V): V {
+    override fun getTargetValue(initialValue: V, initialVelocity: V): V {
         if (!::targetVector.isInitialized) {
             targetVector = initialValue.newInstance()
         }
         for (i in 0 until targetVector.size) {
-            targetVector[i] = floatDecaySpec.getTarget(
+            targetVector[i] = floatDecaySpec.getTargetValue(
                 initialValue[i],
                 initialVelocity[i]
             )
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
index 56592ee..3dd1872 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
@@ -19,12 +19,12 @@
 import androidx.compose.animation.core.AnimationConstants.DefaultDurationMillis
 
 /**
- * [FloatAnimationSpec] interface is similar to [VectorizedAnimationSpec], except it deals exclusively with
- * floats.
+ * [FloatAnimationSpec] interface is similar to [VectorizedAnimationSpec], except it deals
+ * exclusively with floats.
  *
- * Like [VectorizedAnimationSpec], [FloatAnimationSpec] is entirely stateless as well. It requires start/end
- * values and start velocity to be passed in for the query of velocity and value of the animation.
- * The [FloatAnimationSpec] itself stores only the animation configuration (such as the
+ * Like [VectorizedAnimationSpec], [FloatAnimationSpec] is entirely stateless as well. It requires
+ * start/end values and start velocity to be passed in for the query of velocity and value of the
+ * animation. The [FloatAnimationSpec] itself stores only the animation configuration (such as the
  * delay, duration and easing curve for [FloatTweenSpec], or spring constants for
  * [FloatSpringSpec].
  *
@@ -37,32 +37,32 @@
      * Calculates the value of the animation at given the playtime, with the provided start/end
      * values, and start velocity.
      *
-     * @param playTime time since the start of the animation
-     * @param start start value of the animation
-     * @param end end value of the animation
-     * @param startVelocity start velocity of the animation
+     * @param playTimeNanos time since the start of the animation
+     * @param initialValue start value of the animation
+     * @param targetValue end value of the animation
+     * @param initialVelocity start velocity of the animation
      */
-    fun getValue(
-        playTime: Long,
-        start: Float,
-        end: Float,
-        startVelocity: Float
+    fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
     ): Float
 
     /**
      * Calculates the velocity of the animation at given the playtime, with the provided start/end
      * values, and start velocity.
      *
-     * @param playTime time since the start of the animation
-     * @param start start value of the animation
-     * @param end end value of the animation
-     * @param startVelocity start velocity of the animation
+     * @param playTimeNanos time since the start of the animation
+     * @param initialValue start value of the animation
+     * @param targetValue end value of the animation
+     * @param initialVelocity start velocity of the animation
      */
-    fun getVelocity(
-        playTime: Long,
-        start: Float,
-        end: Float,
-        startVelocity: Float
+    fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
     ): Float
 
     /**
@@ -71,33 +71,40 @@
      * animation at the duration time. This is also the default assumption. However, for
      * spring animations, the transient trailing velocity will be snapped to zero.
      *
-     * @param start start value of the animation
-     * @param end end value of the animation
-     * @param startVelocity start velocity of the animation
+     * @param initialValue start value of the animation
+     * @param targetValue end value of the animation
+     * @param initialVelocity start velocity of the animation
      */
     fun getEndVelocity(
-        start: Float,
-        end: Float,
-        startVelocity: Float
-    ): Float = getVelocity(getDurationMillis(start, end, startVelocity), start, end, startVelocity)
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
+    ): Float =
+        getVelocityFromNanos(
+            getDurationNanos(initialValue, targetValue, initialVelocity),
+            initialValue,
+            targetValue,
+            initialVelocity
+        )
 
     /**
      * Calculates the duration of an animation. For duration-based animations, this will return the
      * pre-defined duration. For physics-based animations, the duration will be estimated based on
      * the physics configuration (such as spring stiffness, damping ratio, visibility threshold)
-     * as well as the [start], [end] values, and [startVelocity].
+     * as well as the [initialValue], [targetValue] values, and [initialVelocity].
      *
      * __Note__: this may be a computation that is expensive - especially with spring based
      * animations
      *
-     * @param start start value of the animation
-     * @param end end value of the animation
-     * @param startVelocity start velocity of the animation
+     * @param initialValue start value of the animation
+     * @param targetValue end value of the animation
+     * @param initialVelocity start velocity of the animation
      */
-    fun getDurationMillis(
-        start: Float,
-        end: Float,
-        startVelocity: Float
+    @Suppress("MethodNameUnits")
+    fun getDurationNanos(
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
     ): Long
 
     /**
@@ -130,42 +137,51 @@
         it.stiffness = stiffness
     }
 
-    override fun getValue(
-        playTime: Long,
-        start: Float,
-        end: Float,
-        startVelocity: Float
+    override fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
     ): Float {
-        spring.finalPosition = end
-        val value = spring.updateValues(start, startVelocity, playTime).value
+        // TODO: Properly support Nanos in the spring impl
+        val playTimeMillis = playTimeNanos / MillisToNanos
+        spring.finalPosition = targetValue
+        val value = spring.updateValues(initialValue, initialVelocity, playTimeMillis).value
         return value
     }
 
-    override fun getVelocity(
-        playTime: Long,
-        start: Float,
-        end: Float,
-        startVelocity: Float
+    override fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
     ): Float {
-        spring.finalPosition = end
-        val velocity = spring.updateValues(start, startVelocity, playTime).velocity
+        // TODO: Properly support Nanos in the spring impl
+        val playTimeMillis = playTimeNanos / MillisToNanos
+        spring.finalPosition = targetValue
+        val velocity = spring.updateValues(initialValue, initialVelocity, playTimeMillis).velocity
         return velocity
     }
 
     override fun getEndVelocity(
-        start: Float,
-        end: Float,
-        startVelocity: Float
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
     ): Float = 0f
 
-    override fun getDurationMillis(start: Float, end: Float, startVelocity: Float): Long =
+    @Suppress("MethodNameUnits")
+    override fun getDurationNanos(
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
+    ): Long =
         estimateAnimationDurationMillis(
             stiffness = spring.stiffness,
             dampingRatio = spring.dampingRatio,
-            initialDisplacement = (start - end) / visibilityThreshold,
-            initialVelocity = startVelocity / visibilityThreshold,
+            initialDisplacement = (initialValue - targetValue) / visibilityThreshold,
+            initialVelocity = initialVelocity / visibilityThreshold,
             delta = 1f
-        )
+        ) * MillisToNanos
 }
 
 /**
@@ -184,43 +200,62 @@
     val delay: Int = 0,
     private val easing: Easing = FastOutSlowInEasing
 ) : FloatAnimationSpec {
-    override fun getValue(
-        playTime: Long,
-        start: Float,
-        end: Float,
-        startVelocity: Float
+    override fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
     ): Float {
-        val clampedPlayTime = clampPlayTime(playTime)
+        // TODO: Properly support Nanos in the impl
+        val playTimeMillis = playTimeNanos / MillisToNanos
+        val clampedPlayTime = clampPlayTime(playTimeMillis)
         val rawFraction = if (duration == 0) 1f else clampedPlayTime / duration.toFloat()
         val fraction = easing.transform(rawFraction.coerceIn(0f, 1f))
-        return lerp(start, end, fraction)
+        return lerp(initialValue, targetValue, fraction)
     }
 
     private fun clampPlayTime(playTime: Long): Long {
         return (playTime - delay).coerceIn(0, duration.toLong())
     }
 
-    override fun getDurationMillis(start: Float, end: Float, startVelocity: Float): Long {
-        return delay + duration.toLong()
+    @Suppress("MethodNameUnits")
+    override fun getDurationNanos(
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
+    ): Long {
+        return (delay + duration) * MillisToNanos
     }
 
     // Calculate velocity by difference between the current value and the value 1 ms ago. This is a
     // preliminary way of calculating velocity used by easing curve based animations, and keyframe
     // animations. Physics-based animations give a much more accurate velocity.
-    override fun getVelocity(
-        playTime: Long,
-        start: Float,
-        end: Float,
-        startVelocity: Float
+    override fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
     ): Float {
-        val clampedPlayTime = clampPlayTime(playTime)
+        // TODO: Properly support Nanos in the impl
+        val playTimeMillis = playTimeNanos / MillisToNanos
+        val clampedPlayTime = clampPlayTime(playTimeMillis)
         if (clampedPlayTime < 0) {
             return 0f
         } else if (clampedPlayTime == 0L) {
-            return startVelocity
+            return initialVelocity
         }
-        val startNum = getValue(clampedPlayTime - 1, start, end, startVelocity)
-        val endNum = getValue(clampedPlayTime, start, end, startVelocity)
+        val startNum = getValueFromNanos(
+            (clampedPlayTime - 1) * MillisToNanos,
+            initialValue,
+            targetValue,
+            initialVelocity
+        )
+        val endNum = getValueFromNanos(
+            clampedPlayTime * MillisToNanos,
+            initialValue,
+            targetValue,
+            initialVelocity
+        )
         return (endNum - startNum) * 1000f
     }
 }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
index 2eacf04..98cc051 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
@@ -36,50 +36,51 @@
     /**
      * Returns the value of the animation at the given time.
      *
-     * @param playTime The time elapsed in milliseconds since the start of the animation
-     * @param start The start value of the animation
-     * @param startVelocity The start velocity of the animation
+     * @param playTimeNanos The time elapsed in milliseconds since the start of the animation
+     * @param initialValue The start value of the animation
+     * @param initialVelocity The start velocity of the animation
      */
-    fun getValue(
-        playTime: Long,
-        start: Float,
-        startVelocity: Float
+    fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        initialVelocity: Float
     ): Float
 
     /**
-     * Returns the duration of the decay animation, in milliseconds.
+     * Returns the duration of the decay animation, in nanoseconds.
      *
-     * @param start start value of the animation
-     * @param startVelocity start velocity of the animation
+     * @param initialValue start value of the animation
+     * @param initialVelocity start velocity of the animation
      */
-    fun getDurationMillis(
-        start: Float,
-        startVelocity: Float
+    @Suppress("MethodNameUnits")
+    fun getDurationNanos(
+        initialValue: Float,
+        initialVelocity: Float
     ): Long
 
     /**
      * Returns the velocity of the animation at the given time.
      *
-     * @param playTime The time elapsed in milliseconds since the start of the animation
-     * @param start The start value of the animation
-     * @param startVelocity The start velocity of the animation
+     * @param playTimeNanos The time elapsed in milliseconds since the start of the animation
+     * @param initialValue The start value of the animation
+     * @param initialVelocity The start velocity of the animation
      */
-    fun getVelocity(
-        playTime: Long,
-        start: Float,
-        startVelocity: Float
+    fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        initialVelocity: Float
     ): Float
 
     /**
      * Returns the target value of the animation based on the starting condition of the animation (
      * i.e. start value and start velocity).
      *
-     * @param start The start value of the animation
-     * @param startVelocity The start velocity of the animation
+     * @param initialValue The start value of the animation
+     * @param initialVelocity The start velocity of the animation
      */
-    fun getTarget(
-        start: Float,
-        startVelocity: Float
+    fun getTargetValue(
+        initialValue: Float,
+        initialVelocity: Float
     ): Float
 }
 
@@ -113,40 +114,46 @@
     override val absVelocityThreshold: Float = max(0.0000001f, abs(absVelocityThreshold))
     private val friction: Float = ExponentialDecayFriction * max(0.0001f, frictionMultiplier)
 
-    override fun getValue(
-        playTime: Long,
-        start: Float,
-        startVelocity: Float
+    override fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        initialVelocity: Float
     ): Float {
-        return start - startVelocity / friction +
-            startVelocity / friction * exp(friction * playTime / 1000f)
+        // TODO: Properly support nanos
+        val playTimeMillis = playTimeNanos / MillisToNanos
+        return initialValue - initialVelocity / friction +
+            initialVelocity / friction * exp(friction * playTimeMillis / 1000f)
     }
 
-    override fun getVelocity(
-        playTime: Long,
-        start: Float,
-        startVelocity: Float
+    override fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        initialVelocity: Float
     ): Float {
-        return (startVelocity * exp(((playTime / 1000f) * friction)))
+        // TODO: Properly support nanos
+        val playTimeMillis = playTimeNanos / MillisToNanos
+        return (initialVelocity * exp(((playTimeMillis / 1000f) * friction)))
     }
 
-    override fun getDurationMillis(start: Float, startVelocity: Float): Long {
+    @Suppress("MethodNameUnits")
+    override fun getDurationNanos(initialValue: Float, initialVelocity: Float): Long {
         // Inverse of getVelocity
-        return (1000f * ln(absVelocityThreshold / abs(startVelocity)) / friction).toLong()
+        return (1000f * ln(absVelocityThreshold / abs(initialVelocity)) / friction)
+            .toLong() * MillisToNanos
     }
 
-    override fun getTarget(
-        start: Float,
-        startVelocity: Float
+    override fun getTargetValue(
+        initialValue: Float,
+        initialVelocity: Float
     ): Float {
-        if (abs(startVelocity) <= absVelocityThreshold) {
-            return start
+        if (abs(initialVelocity) <= absVelocityThreshold) {
+            return initialValue
         }
         val duration: Double =
-            ln(abs(absVelocityThreshold / startVelocity).toDouble()) / friction * 1000
+            ln(abs(absVelocityThreshold / initialVelocity).toDouble()) / friction * 1000
 
-        return start - startVelocity / friction +
-            startVelocity / friction * exp((friction * duration / 1000f)).toFloat()
+        return initialValue - initialVelocity / friction +
+            initialVelocity / friction * exp((friction * duration / 1000f)).toFloat()
     }
 }
 
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
index dd37aaa..f1c9d3a 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
@@ -106,9 +106,9 @@
                 startOnTheNextFrame = false
                 playTimeNanosOffset = playTimeNanos
             }
-            val playTimeMillis = (playTimeNanos - playTimeNanosOffset) / 1_000_000L
-            value = animation.getValue(playTimeMillis)
-            isFinished = animation.isFinished(playTimeMillis)
+            val playTime = playTimeNanos - playTimeNanosOffset
+            value = animation.getValueFromNanos(playTime)
+            isFinished = animation.isFinishedFromNanos(playTime)
         }
     }
 
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 5594bfd..5ad9c06 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
@@ -221,8 +221,8 @@
     startTimeNanos: Long = AnimationConstants.UnspecifiedTime,
     block: AnimationScope<T, V>.() -> Unit = {}
 ) {
-    val initialValue = animation.getValue(0)
-    val initialVelocityVector = animation.getVelocityVector(0)
+    val initialValue = animation.getValueFromNanos(0)
+    val initialVelocityVector = animation.getVelocityVectorFromNanos(0)
     var lateInitScope: AnimationScope<T, V>? = null
     try {
         val startTimeNanosSpecified =
@@ -278,11 +278,10 @@
     block: AnimationScope<T, V>.() -> Unit
 ) {
     lastFrameTimeNanos = frameTimeNanos
-    val playTimeMillis = (frameTimeNanos - startTimeNanos) / 1_000_000L
-    // TODO: [Animation] should use nanos for all the value/velocity queries
-    value = anim.getValue(playTimeMillis)
-    velocityVector = anim.getVelocityVector(playTimeMillis)
-    val isLastFrame = anim.isFinished(playTimeMillis)
+    val playTimeNanos = frameTimeNanos - startTimeNanos
+    value = anim.getValueFromNanos(playTimeNanos)
+    velocityVector = anim.getVelocityVectorFromNanos(playTimeNanos)
+    val isLastFrame = anim.isFinishedFromNanos(playTimeNanos)
     if (isLastFrame) {
         // TODO: This could probably be a little more granular
         // TODO: end time isn't necessarily last frame time
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 fb21aa8..d7b4510 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
@@ -375,13 +375,13 @@
             internal set
         internal var velocityVector: V = initialVelocityVector
         internal val durationNanos
-            get() = animation.durationMillis.times(1_000_000L)
+            get() = animation.durationNanos
 
         internal fun onPlayTimeChanged(playTimeNanos: Long) {
-            val playTimeMillis = (playTimeNanos - offsetTimeNanos) / 1_000_000L
-            value = animation.getValue(playTimeMillis)
-            velocityVector = animation.getVelocityVector(playTimeMillis)
-            if (animation.isFinished(playTimeMillis)) {
+            val playTime = playTimeNanos - offsetTimeNanos
+            value = animation.getValueFromNanos(playTime)
+            velocityVector = animation.getVelocityVectorFromNanos(playTime)
+            if (animation.isFinishedFromNanos(playTime)) {
                 isFinished = true
                 offsetTimeNanos = 0
             }
@@ -390,9 +390,8 @@
         internal fun seekTo(playTimeNanos: Long) {
             // TODO: unlikely but need to double check that animation returns the correct values
             // when play time is way past their durations.
-            val playTimeMillis = playTimeNanos / 1_000_000L
-            value = animation.getValue(playTimeMillis)
-            velocityVector = animation.getVelocityVector(playTimeMillis)
+            value = animation.getValueFromNanos(playTimeNanos)
+            velocityVector = animation.getVelocityVectorFromNanos(playTimeNanos)
         }
 
         private fun updateAnimation(initialValue: T = value) {
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 5b5677a..f9d455c 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
@@ -31,7 +31,7 @@
  * Since [VectorizedAnimationSpec]s are stateless, it requires starting value/velocity and ending
  * value to be passed in, along with playtime, to calculate the value or velocity at that time. Play
  * time here is the progress of the animation in terms of milliseconds, where 0 means the start
- * of the animation and [getDurationMillis] returns the play time for the end of the animation.
+ * of the animation and [getDurationNanos] returns the play time for the end of the animation.
  *
  * __Note__: For use cases where the starting values/velocity and ending values aren't expected
  * to change, it is recommended to use [Animation] that caches these static values and hence
@@ -51,48 +51,49 @@
      * Calculates the value of the animation at given the playtime, with the provided start/end
      * values, and start velocity.
      *
-     * @param playTime time since the start of the animation
-     * @param start start value of the animation
-     * @param end end value of the animation
-     * @param startVelocity start velocity of the animation
+     * @param playTimeNanos time since the start of the animation
+     * @param initialValue start value of the animation
+     * @param targetValue end value of the animation
+     * @param initialVelocity start velocity of the animation
      */
-    fun getValue(
-        playTime: Long,
-        start: V,
-        end: V,
-        startVelocity: V
+    fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
     ): V
 
     /**
      * Calculates the velocity of the animation at given the playtime, with the provided start/end
      * values, and start velocity.
      *
-     * @param playTime time since the start of the animation
-     * @param start start value of the animation
-     * @param end end value of the animation
-     * @param startVelocity start velocity of the animation
+     * @param playTimeNanos time since the start of the animation
+     * @param initialValue start value of the animation
+     * @param targetValue end value of the animation
+     * @param initialVelocity start velocity of the animation
      */
-    fun getVelocity(
-        playTime: Long,
-        start: V,
-        end: V,
-        startVelocity: V
+    fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
     ): V
 
     /**
      * Calculates the duration of an animation. For duration-based animations, this will return the
      * pre-defined duration. For physics-based animations, the duration will be estimated based on
      * the physics configuration (such as spring stiffness, damping ratio, visibility threshold)
-     * as well as the [start], [end] values, and [startVelocity].
+     * as well as the [initialValue], [targetValue] values, and [initialVelocity].
      *
-     * @param start start value of the animation
-     * @param end end value of the animation
-     * @param startVelocity start velocity of the animation
+     * @param initialValue start value of the animation
+     * @param targetValue end value of the animation
+     * @param initialVelocity start velocity of the animation
      */
-    fun getDurationMillis(
-        start: V,
-        end: V,
-        startVelocity: V
+    @Suppress("MethodNameUnits")
+    fun getDurationNanos(
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
     ): Long
 
     /**
@@ -101,18 +102,56 @@
      * animation at the duration time. This is also the default assumption. However, for
      * physics-based animations, end velocity is an [AnimationVector] of 0s.
      *
-     * @param start start value of the animation
-     * @param end end value of the animation
-     * @param startVelocity start velocity of the animation
+     * @param initialValue start value of the animation
+     * @param targetValue end value of the animation
+     * @param initialVelocity start velocity of the animation
      */
     fun getEndVelocity(
-        start: V,
-        end: V,
-        startVelocity: V
-    ): V = getVelocity(getDurationMillis(start, end, startVelocity), start, end, startVelocity)
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
+    ): V = getVelocityFromNanos(
+        getDurationNanos(initialValue, targetValue, initialVelocity),
+        initialValue,
+        targetValue,
+        initialVelocity
+    )
 }
 
 /**
+ * Calculates the duration of an animation. For duration-based animations, this will return the
+ * pre-defined duration. For physics-based animations, the duration will be estimated based on
+ * the physics configuration (such as spring stiffness, damping ratio, visibility threshold)
+ * as well as the [initialValue], [targetValue] values, and [initialVelocity].
+ *
+ * @param initialValue start value of the animation
+ * @param targetValue end value of the animation
+ * @param initialVelocity start velocity of the animation
+ */
+internal fun <V : AnimationVector> VectorizedAnimationSpec<V>.getDurationMillis(
+    initialValue: V,
+    targetValue: V,
+    initialVelocity: V
+): Long = getDurationNanos(initialValue, targetValue, initialVelocity) / MillisToNanos
+
+/**
+ * Calculates the value of the animation at given the playtime, with the provided start/end
+ * values, and start velocity.
+ *
+ * @param playTimeMillis time since the start of the animation
+ * @param start start value of the animation
+ * @param end end value of the animation
+ * @param startVelocity start velocity of the animation
+ */
+// TODO: Move tests off this API
+internal fun <V : AnimationVector> VectorizedAnimationSpec<V>.getValueFromMillis(
+    playTimeMillis: Long,
+    start: V,
+    end: V,
+    startVelocity: V
+): V = getValueFromNanos(playTimeMillis * MillisToNanos, start, end, startVelocity)
+
+/**
  * All the finite [VectorizedAnimationSpec]s implement this interface, including:
  * [VectorizedKeyframesSpec], [VectorizedTweenSpec], [VectorizedRepeatableSpec],
  * [VectorizedSnapSpec], [VectorizedSpringSpec], etc. The [VectorizedAnimationSpec] that does
@@ -137,8 +176,9 @@
      */
     val delayMillis: Int
 
-    override fun getDurationMillis(start: V, end: V, startVelocity: V): Long =
-        (delayMillis + durationMillis).toLong()
+    @Suppress("MethodNameUnits")
+    override fun getDurationNanos(initialValue: V, targetValue: V, initialVelocity: V): Long =
+        (delayMillis + durationMillis) * MillisToNanos
 }
 
 /**
@@ -188,25 +228,26 @@
     private lateinit var valueVector: V
     private lateinit var velocityVector: V
 
-    override fun getValue(
-        playTime: Long,
-        start: V,
-        end: V,
-        startVelocity: V
+    override fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
     ): V {
-        val clampedPlayTime = clampPlayTime(playTime).toInt()
+        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 (clampedPlayTime >= durationMillis) {
-            return end
-        } else if (clampedPlayTime <= 0) return start
+            return targetValue
+        } else if (clampedPlayTime <= 0) return initialValue
 
         var startTime = 0
-        var startVal = start
-        var endVal = end
+        var startVal = initialValue
+        var endVal = targetValue
         var endTime: Int = durationMillis
         var easing: Easing = LinearEasing
         for ((timestamp, value) in keyframes) {
@@ -224,7 +265,7 @@
         val fraction = easing.transform(
             (clampedPlayTime - startTime) / (endTime - startTime).toFloat()
         )
-        init(start)
+        init(initialValue)
         for (i in 0 until startVal.size) {
             valueVector[i] = lerp(startVal[i], endVal[i], fraction)
         }
@@ -238,20 +279,31 @@
         }
     }
 
-    override fun getVelocity(
-        playTime: Long,
-        start: V,
-        end: V,
-        startVelocity: V
+    override fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
     ): V {
-        val clampedPlayTime = clampPlayTime(playTime)
+        val playTimeMillis = playTimeNanos / MillisToNanos
+        val clampedPlayTime = clampPlayTime(playTimeMillis)
         if (clampedPlayTime <= 0L) {
-            return startVelocity
+            return initialVelocity
         }
-        val startNum = getValue(clampedPlayTime - 1, start, end, startVelocity)
-        val endNum = getValue(clampedPlayTime, start, end, startVelocity)
+        val startNum = getValueFromMillis(
+            clampedPlayTime - 1,
+            initialValue,
+            targetValue,
+            initialVelocity
+        )
+        val endNum = getValueFromMillis(
+            clampedPlayTime,
+            initialValue,
+            targetValue,
+            initialVelocity
+        )
 
-        init(start)
+        init(initialValue)
         for (i in 0 until startNum.size) {
             velocityVector[i] = (startNum[i] - endNum[i]) * 1000f
         }
@@ -269,16 +321,26 @@
     override val delayMillis: Int = 0
 ) : VectorizedDurationBasedAnimationSpec<V> {
 
-    override fun getValue(playTime: Long, start: V, end: V, startVelocity: V): V {
-        if (playTime < delayMillis) {
-            return start
+    override fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
+    ): V {
+        if (playTimeNanos < delayMillis * MillisToNanos) {
+            return initialValue
         } else {
-            return end
+            return targetValue
         }
     }
 
-    override fun getVelocity(playTime: Long, start: V, end: V, startVelocity: V): V {
-        return startVelocity
+    override fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
+    ): V {
+        return initialVelocity
     }
 
     override val durationMillis: Int
@@ -329,55 +391,61 @@
         }
     }
 
-    internal val duration: Int = animation.delayMillis + animation.durationMillis
+    internal val durationNanos: Long =
+        (animation.delayMillis + animation.durationMillis) * MillisToNanos
 
-    private fun repetitionPlayTime(playTime: Long): Long {
-        val repeatsCount = min(playTime / duration, iterations - 1L)
+    private fun repetitionPlayTimeNanos(playTimeNanos: Long): Long {
+        val repeatsCount = min(playTimeNanos / durationNanos, iterations - 1L)
         if (repeatMode == RepeatMode.Restart || repeatsCount % 2 == 0L) {
-            return playTime - repeatsCount * duration
+            return playTimeNanos - repeatsCount * durationNanos
         } else {
-            return (repeatsCount + 1) * duration - playTime
+            return (repeatsCount + 1) * durationNanos - playTimeNanos
         }
     }
 
-    private fun repetitionStartVelocity(playTime: Long, start: V, startVelocity: V, end: V): V =
-        if (playTime > duration) {
-            // Start velocity of the 2nd and subsequent iteration will be the velocity at the end
-            // of the first iteration, instead of the initial velocity.
-            getVelocity(duration.toLong(), start, startVelocity, end)
-        } else
-            startVelocity
-
-    override fun getValue(
-        playTime: Long,
+    private fun repetitionStartVelocity(
+        playTimeNanos: Long,
         start: V,
-        end: V,
-        startVelocity: V
+        startVelocity: V,
+        end: V
+    ): V = if (playTimeNanos > durationNanos) {
+        // Start velocity of the 2nd and subsequent iteration will be the velocity at the end
+        // of the first iteration, instead of the initial velocity.
+        getVelocityFromNanos(durationNanos, start, startVelocity, end)
+    } else
+        startVelocity
+
+    override fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
     ): V {
-        return animation.getValue(
-            repetitionPlayTime(playTime),
-            start,
-            end,
-            repetitionStartVelocity(playTime, start, startVelocity, end)
+        return animation.getValueFromNanos(
+            repetitionPlayTimeNanos(playTimeNanos),
+            initialValue,
+            targetValue,
+            repetitionStartVelocity(playTimeNanos, initialValue, initialVelocity, targetValue)
         )
     }
 
-    override fun getVelocity(
-        playTime: Long,
-        start: V,
-        end: V,
-        startVelocity: V
+    override fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
     ): V {
-        return animation.getVelocity(
-            repetitionPlayTime(playTime),
-            start,
-            end,
-            repetitionStartVelocity(playTime, start, startVelocity, end)
+        return animation.getVelocityFromNanos(
+            repetitionPlayTimeNanos(playTimeNanos),
+            initialValue,
+            targetValue,
+            repetitionStartVelocity(playTimeNanos, initialValue, initialVelocity, targetValue)
         )
     }
 
-    override fun getDurationMillis(start: V, end: V, startVelocity: V): Long {
-        return iterations * duration.toLong()
+    @Suppress("MethodNameUnits")
+    override fun getDurationNanos(initialValue: V, targetValue: V, initialVelocity: V): Long {
+        return iterations * durationNanos
     }
 }
 
@@ -517,12 +585,22 @@
         FloatTweenSpec(durationMillis, delayMillis, easing)
     )
 
-    override fun getValue(playTime: Long, start: V, end: V, startVelocity: V): V {
-        return anim.getValue(playTime, start, end, startVelocity)
+    override fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
+    ): V {
+        return anim.getValueFromNanos(playTimeNanos, initialValue, targetValue, initialVelocity)
     }
 
-    override fun getVelocity(playTime: Long, start: V, end: V, startVelocity: V): V {
-        return anim.getVelocity(playTime, start, end, startVelocity)
+    override fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
+    ): V {
+        return anim.getVelocityFromNanos(playTimeNanos, initialValue, targetValue, initialVelocity)
     }
 }
 
@@ -550,43 +628,65 @@
         }
     })
 
-    override fun getValue(playTime: Long, start: V, end: V, startVelocity: V): V {
+    override fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
+    ): V {
         if (!::valueVector.isInitialized) {
-            valueVector = start.newInstance()
+            valueVector = initialValue.newInstance()
         }
         for (i in 0 until valueVector.size) {
-            valueVector[i] = anims[i].getValue(playTime, start[i], end[i], startVelocity[i])
+            valueVector[i] = anims[i].getValueFromNanos(
+                playTimeNanos,
+                initialValue[i],
+                targetValue[i],
+                initialVelocity[i]
+            )
         }
         return valueVector
     }
 
-    override fun getVelocity(playTime: Long, start: V, end: V, startVelocity: V): V {
+    override fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: V,
+        targetValue: V,
+        initialVelocity: V
+    ): V {
         if (!::velocityVector.isInitialized) {
-            velocityVector = startVelocity.newInstance()
+            velocityVector = initialVelocity.newInstance()
         }
         for (i in 0 until velocityVector.size) {
-            velocityVector[i] = anims[i].getVelocity(playTime, start[i], end[i], startVelocity[i])
+            velocityVector[i] =
+                anims[i].getVelocityFromNanos(
+                    playTimeNanos,
+                    initialValue[i],
+                    targetValue[i],
+                    initialVelocity[i]
+                )
         }
         return velocityVector
     }
 
-    override fun getEndVelocity(start: V, end: V, startVelocity: V): V {
+    override fun getEndVelocity(initialValue: V, targetValue: V, initialVelocity: V): V {
         if (!::endVelocityVector.isInitialized) {
-            endVelocityVector = startVelocity.newInstance()
+            endVelocityVector = initialVelocity.newInstance()
         }
         for (i in 0 until endVelocityVector.size) {
             endVelocityVector[i] =
-                anims[i].getEndVelocity(start[i], end[i], startVelocity[i])
+                anims[i].getEndVelocity(initialValue[i], targetValue[i], initialVelocity[i])
         }
         return endVelocityVector
     }
 
-    override fun getDurationMillis(start: V, end: V, startVelocity: V): Long {
+    @Suppress("MethodNameUnits")
+    override fun getDurationNanos(initialValue: V, targetValue: V, initialVelocity: V): Long {
         var maxDuration = 0L
-        (0 until start.size).forEach {
+        (0 until initialValue.size).forEach {
             maxDuration = maxOf(
                 maxDuration,
-                anims[it].getDurationMillis(start[it], end[it], startVelocity[it])
+                anims[it].getDurationNanos(initialValue[it], targetValue[it], initialVelocity[it])
             )
         }
         return maxDuration
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt
index fba75bb..e893ad2 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt
@@ -26,7 +26,7 @@
  * Since [VectorizedDecayAnimationSpec]s are stateless, it requires starting value/velocity and
  * ending value to be passed in, along with playtime, to calculate the value or velocity at that
  * time. Play time here is the progress of the animation in terms of milliseconds, where 0 means the
- * start of the animation and [getDurationMillis] returns the play time for the end of the
+ * start of the animation and [getDurationNanos] returns the play time for the end of the
  * animation.
  *
  * __Note__: For use cases where the starting values/velocity and ending values aren't expected
@@ -45,23 +45,24 @@
     /**
      * Returns the value of the animation at the given time.
      *
-     * @param playTime The time elapsed in milliseconds since the initialValue of the animation
+     * @param playTimeNanos The time elapsed in milliseconds since the initialValue of the animation
      * @param initialValue The initialValue value of the animation
      * @param initialVelocity The initialValue velocity of the animation
      */
-    fun getValue(
-        playTime: Long,
+    fun getValueFromNanos(
+        playTimeNanos: Long,
         initialValue: V,
         initialVelocity: V
     ): V
 
     /**
-     * Returns the duration of the decay animation, in milliseconds.
+     * Returns the duration of the decay animation, in nanoseconds.
      *
      * @param initialValue initialValue value of the animation
      * @param initialVelocity initialValue velocity of the animation
      */
-    fun getDurationMillis(
+    @Suppress("MethodNameUnits")
+    fun getDurationNanos(
         initialValue: V,
         initialVelocity: V
     ): Long
@@ -69,12 +70,12 @@
     /**
      * Returns the velocity of the animation at the given time.
      *
-     * @param playTime The time elapsed in milliseconds since the initialValue of the animation
+     * @param playTimeNanos The time elapsed in milliseconds since the initialValue of the animation
      * @param initialValue The initialValue value of the animation
      * @param initialVelocity The initialValue velocity of the animation
      */
-    fun getVelocity(
-        playTime: Long,
+    fun getVelocityFromNanos(
+        playTimeNanos: Long,
         initialValue: V,
         initialVelocity: V
     ): V
@@ -86,7 +87,7 @@
      * @param initialValue The initial value of the animation
      * @param initialVelocity The initial velocity of the animation
      */
-    fun getTarget(
+    fun getTargetValue(
         initialValue: V,
         initialVelocity: V
     ): V
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimatableTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimatableTest.kt
index 3f9d010..0cdf8da 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimatableTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimatableTest.kt
@@ -60,8 +60,12 @@
                 val result = animatable.animateDecay(20f, animationSpec = exponentialDecay()) {
                     assertTrue(isRunning)
                     assertEquals(anim.targetValue, targetValue)
-                    TestCase.assertEquals(anim.getValue(playTimeMillis), value, 0.001f)
-                    TestCase.assertEquals(anim.getVelocity(playTimeMillis), velocity, 0.001f)
+                    TestCase.assertEquals(anim.getValueFromMillis(playTimeMillis), value, 0.001f)
+                    TestCase.assertEquals(
+                        anim.getVelocityFromMillis(playTimeMillis),
+                        velocity,
+                        0.001f
+                    )
                     playTimeMillis += interval
                     TestCase.assertEquals(value, animatable.value, 0.0001f)
                     TestCase.assertEquals(velocity, animatable.velocity, 0.0001f)
@@ -98,8 +102,8 @@
                 ) {
                     assertTrue(isRunning)
                     assertEquals(1f, targetValue)
-                    assertEquals(anim.getValue(playTimeMillis), value, 0.001f)
-                    assertEquals(anim.getVelocity(playTimeMillis), velocity, 0.001f)
+                    assertEquals(anim.getValueFromMillis(playTimeMillis), value, 0.001f)
+                    assertEquals(anim.getVelocityFromMillis(playTimeMillis), velocity, 0.001f)
                     playTimeMillis += interval
                 }
                 // After animation
@@ -146,7 +150,7 @@
                         ) {
                             assertTrue("PlayTime Millis: $playTimeMillis", isRunning)
                             assertEquals(to, targetValue)
-                            val expectedValue = anim.getValue(playTimeMillis)
+                            val expectedValue = anim.getValueFromMillis(playTimeMillis)
                             assertEquals(
                                 "PlayTime Millis: $playTimeMillis",
                                 expectedValue.x,
@@ -207,8 +211,8 @@
                         ) {
                             assertTrue(isRunning)
                             assertEquals(targetValue, 200f)
-                            assertEquals(anim1.getValue(playTimeMillis), value)
-                            assertEquals(anim1.getVelocity(playTimeMillis), velocity)
+                            assertEquals(anim1.getValueFromMillis(playTimeMillis), value)
+                            assertEquals(anim1.getVelocityFromMillis(playTimeMillis), velocity)
 
                             assertTrue(playTimeMillis <= 100)
                             if (playTimeMillis == 100L) {
@@ -252,11 +256,11 @@
                 assertTrue(isRunning)
                 assertEquals(300f, targetValue)
                 assertEquals(
-                    anim2.getValue((playTimeMillis2 - 100)),
+                    anim2.getValueFromMillis((playTimeMillis2 - 100)),
                     value
                 )
                 assertEquals(
-                    anim2.getVelocity((playTimeMillis2 - 100)),
+                    anim2.getVelocityFromMillis((playTimeMillis2 - 100)),
                     velocity
                 )
                 playTimeMillis2 += interval
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt
index 614cc8a..3321867 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt
@@ -41,30 +41,66 @@
 
         val start1D = AnimationVector(0f)
         val end1D = AnimationVector(1f)
-        assertEquals(start1D, snap1D.getValue(0L, start1D, end1D, start1D))
-        assertEquals(start1D, snap1D.getValue(snap1D.delayMillis - 1L, start1D, end1D, start1D))
-        assertEquals(end1D, snap1D.getValue(snap1D.delayMillis.toLong(), start1D, end1D, start1D))
-        assertEquals(end1D, snap1D.getValue(snap1D.delayMillis + 100L, start1D, end1D, start1D))
+        assertEquals(start1D, snap1D.getValueFromMillis(0L, start1D, end1D, start1D))
+        assertEquals(
+            start1D,
+            snap1D.getValueFromMillis(snap1D.delayMillis - 1L, start1D, end1D, start1D)
+        )
+        assertEquals(
+            end1D,
+            snap1D.getValueFromMillis(snap1D.delayMillis.toLong(), start1D, end1D, start1D)
+        )
+        assertEquals(
+            end1D,
+            snap1D.getValueFromMillis(snap1D.delayMillis + 100L, start1D, end1D, start1D)
+        )
 
         val start2D = AnimationVector(0f, 50f)
         val end2D = AnimationVector(1f, -50f)
-        assertEquals(start2D, snap2D.getValue(0L, start2D, end2D, start2D))
-        assertEquals(start2D, snap2D.getValue(snap2D.delayMillis - 1L, start2D, end2D, start2D))
-        assertEquals(end2D, snap2D.getValue(snap2D.delayMillis.toLong(), start2D, end2D, start2D))
-        assertEquals(end2D, snap2D.getValue(snap2D.delayMillis + 100L, start2D, end2D, start2D))
+        assertEquals(start2D, snap2D.getValueFromMillis(0L, start2D, end2D, start2D))
+        assertEquals(
+            start2D,
+            snap2D.getValueFromMillis(snap2D.delayMillis - 1L, start2D, end2D, start2D)
+        )
+        assertEquals(
+            end2D,
+            snap2D.getValueFromMillis(snap2D.delayMillis.toLong(), start2D, end2D, start2D)
+        )
+        assertEquals(
+            end2D,
+            snap2D.getValueFromMillis(snap2D.delayMillis + 100L, start2D, end2D, start2D)
+        )
 
         val start3D = AnimationVector(0f, 20f, -100f)
         val end3D = AnimationVector(-40f, 0f, 200f)
-        assertEquals(start3D, snap3D.getValue(snap3D.delayMillis - 1L, start3D, end3D, start3D))
-        assertEquals(end3D, snap3D.getValue(snap3D.delayMillis.toLong(), start3D, end3D, start3D))
-        assertEquals(end3D, snap3D.getValue(snap3D.delayMillis + 100L, start3D, end3D, start3D))
+        assertEquals(
+            start3D,
+            snap3D.getValueFromMillis(snap3D.delayMillis - 1L, start3D, end3D, start3D)
+        )
+        assertEquals(
+            end3D,
+            snap3D.getValueFromMillis(snap3D.delayMillis.toLong(), start3D, end3D, start3D)
+        )
+        assertEquals(
+            end3D,
+            snap3D.getValueFromMillis(snap3D.delayMillis + 100L, start3D, end3D, start3D)
+        )
 
         val start4D = AnimationVector(48f, 26f, 88f, 177f)
         val end4D = AnimationVector(64f, 286f, -999f, 40f)
-        assertEquals(start4D, snap4D.getValue(0L, start4D, end4D, start4D))
-        assertEquals(start4D, snap4D.getValue(snap4D.delayMillis - 1L, start4D, end4D, start4D))
-        assertEquals(end4D, snap4D.getValue(snap4D.delayMillis.toLong(), start4D, end4D, start4D))
-        assertEquals(end4D, snap4D.getValue(snap4D.delayMillis + 100L, start4D, end4D, start4D))
+        assertEquals(start4D, snap4D.getValueFromMillis(0L, start4D, end4D, start4D))
+        assertEquals(
+            start4D,
+            snap4D.getValueFromMillis(snap4D.delayMillis - 1L, start4D, end4D, start4D)
+        )
+        assertEquals(
+            end4D,
+            snap4D.getValueFromMillis(snap4D.delayMillis.toLong(), start4D, end4D, start4D)
+        )
+        assertEquals(
+            end4D,
+            snap4D.getValueFromMillis(snap4D.delayMillis + 100L, start4D, end4D, start4D)
+        )
     }
 
     @Test
@@ -81,10 +117,13 @@
             delay
         )
 
-        assertEquals(startValue, keyframes.getValue(0L, startValue, endValue, startValue))
         assertEquals(
             startValue,
-            keyframes.getValue(delay.toLong(), startValue, endValue, startValue)
+            keyframes.getValueFromMillis(0L, startValue, endValue, startValue)
+        )
+        assertEquals(
+            startValue,
+            keyframes.getValueFromMillis(delay.toLong(), startValue, endValue, startValue)
         )
         for (i in 0..200 step 50) {
             val fraction: Float
@@ -100,7 +139,7 @@
             )
             assertEquals(
                 animValue,
-                keyframes.getValue(
+                keyframes.getValueFromMillis(
                     delay + i.toLong(),
                     startValue, endValue, startValue
                 )
@@ -108,7 +147,10 @@
         }
 
         // Test playtime > duration + delay
-        assertEquals(endValue, keyframes.getValue(500L, startValue, endValue, startValue))
+        assertEquals(
+            endValue,
+            keyframes.getValueFromMillis(500L, startValue, endValue, startValue)
+        )
     }
 
     @Test
@@ -122,11 +164,14 @@
         // 1D vector
         val start1D = AnimationVector(0f)
         val end1D = AnimationVector(1f)
-        assertEquals(start1D, tween1D.getValue(0L, start1D, end1D, start1D))
-        assertEquals(start1D, tween1D.getValue(tween1D.delayMillis - 1L, start1D, end1D, start1D))
+        assertEquals(start1D, tween1D.getValueFromMillis(0L, start1D, end1D, start1D))
         assertEquals(
             start1D,
-            tween1D.getValue(tween1D.delayMillis.toLong(), start1D, end1D, start1D)
+            tween1D.getValueFromMillis(tween1D.delayMillis - 1L, start1D, end1D, start1D)
+        )
+        assertEquals(
+            start1D,
+            tween1D.getValueFromMillis(tween1D.delayMillis.toLong(), start1D, end1D, start1D)
         )
         val animValue1D = AnimationVector(
             lerp(
@@ -136,17 +181,20 @@
         )
         assertEquals(
             animValue1D,
-            tween1D.getValue(tween1D.delayMillis + 100L, start1D, end1D, start1D)
+            tween1D.getValueFromMillis(tween1D.delayMillis + 100L, start1D, end1D, start1D)
         )
 
         // 2D vector
         val start2D = AnimationVector(0f, 50f)
         val end2D = AnimationVector(1f, -50f)
-        assertEquals(start2D, tween2D.getValue(0L, start2D, end2D, start2D))
-        assertEquals(start2D, tween2D.getValue(tween2D.delayMillis - 1L, start2D, end2D, start2D))
+        assertEquals(start2D, tween2D.getValueFromMillis(0L, start2D, end2D, start2D))
         assertEquals(
             start2D,
-            tween2D.getValue(tween2D.delayMillis.toLong(), start2D, end2D, start2D)
+            tween2D.getValueFromMillis(tween2D.delayMillis - 1L, start2D, end2D, start2D)
+        )
+        assertEquals(
+            start2D,
+            tween2D.getValueFromMillis(tween2D.delayMillis.toLong(), start2D, end2D, start2D)
         )
         val animValue2D = AnimationVector(
             lerp(start2D.v1, end2D.v1, 100f / tween2D.durationMillis),
@@ -154,17 +202,20 @@
         )
         assertEquals(
             animValue2D,
-            tween2D.getValue(tween2D.delayMillis + 100L, start2D, end2D, start2D)
+            tween2D.getValueFromMillis(tween2D.delayMillis + 100L, start2D, end2D, start2D)
         )
 
         // 3D Vector
         val start3D = AnimationVector(0f, 20f, -100f)
         val end3D = AnimationVector(-40f, 0f, 200f)
-        assertEquals(start3D, tween3D.getValue(0L, start3D, end3D, start3D))
-        assertEquals(start3D, tween3D.getValue(tween3D.delayMillis - 1L, start3D, end3D, start3D))
+        assertEquals(start3D, tween3D.getValueFromMillis(0L, start3D, end3D, start3D))
         assertEquals(
             start3D,
-            tween3D.getValue(tween3D.delayMillis.toLong(), start3D, end3D, start3D)
+            tween3D.getValueFromMillis(tween3D.delayMillis - 1L, start3D, end3D, start3D)
+        )
+        assertEquals(
+            start3D,
+            tween3D.getValueFromMillis(tween3D.delayMillis.toLong(), start3D, end3D, start3D)
         )
         val animValue3D = AnimationVector(
             lerp(
@@ -185,17 +236,20 @@
         )
         assertEquals(
             animValue3D,
-            tween3D.getValue(tween3D.delayMillis + 100L, start3D, end3D, start3D)
+            tween3D.getValueFromMillis(tween3D.delayMillis + 100L, start3D, end3D, start3D)
         )
 
         // 4D Vector
         val start4D = AnimationVector(48f, 26f, 88f, 177f)
         val end4D = AnimationVector(64f, 286f, -999f, 40f)
-        assertEquals(start4D, tween4D.getValue(0L, start4D, end4D, start4D))
-        assertEquals(start4D, tween4D.getValue(tween4D.delayMillis - 1L, start4D, end4D, start4D))
+        assertEquals(start4D, tween4D.getValueFromMillis(0L, start4D, end4D, start4D))
         assertEquals(
             start4D,
-            tween4D.getValue(tween4D.delayMillis.toLong(), start4D, end4D, start4D)
+            tween4D.getValueFromMillis(tween4D.delayMillis - 1L, start4D, end4D, start4D)
+        )
+        assertEquals(
+            start4D,
+            tween4D.getValueFromMillis(tween4D.delayMillis.toLong(), start4D, end4D, start4D)
         )
         val animValue4D = AnimationVector(
             lerp(
@@ -217,7 +271,7 @@
         )
         assertEquals(
             animValue4D,
-            tween4D.getValue(tween4D.delayMillis + 100L, start4D, end4D, start4D)
+            tween4D.getValueFromMillis(tween4D.delayMillis + 100L, start4D, end4D, start4D)
         )
     }
 
@@ -248,11 +302,11 @@
         for (i in 0..duration step 100) {
             assertEquals(
                 AnimationVector(
-                    floatAnim.getValue(i, start.v1, end.v1, startVelocity.v1),
-                    floatAnim.getValue(i, start.v2, end.v2, startVelocity.v2),
-                    floatAnim.getValue(i, start.v3, end.v3, startVelocity.v3)
+                    floatAnim.getValueFromMillis(i, start.v1, end.v1, startVelocity.v1),
+                    floatAnim.getValueFromMillis(i, start.v2, end.v2, startVelocity.v2),
+                    floatAnim.getValueFromMillis(i, start.v3, end.v3, startVelocity.v3)
                 ),
-                anim3D.getValue(i, start, end, startVelocity)
+                anim3D.getValueFromMillis(i, start, end, startVelocity)
             )
         }
     }
@@ -304,13 +358,13 @@
         )
         for (playtime in 0..fixedAnim.durationMillis step 100) {
             assertEquals(
-                anim.getValue(playtime, start, end, startVelocity),
-                fixedAnim.getValue(playtime)
+                anim.getValueFromMillis(playtime, start, end, startVelocity),
+                fixedAnim.getValueFromMillis(playtime)
             )
 
             assertEquals(
-                anim.getVelocity(playtime, start, end, startVelocity),
-                fixedAnim.getVelocity(playtime)
+                anim.getVelocityFromNanos(playtime * MillisToNanos, start, end, startVelocity),
+                fixedAnim.getVelocityFromMillis(playtime)
             )
         }
         assertEquals(
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt
index 93d1917..fcb63dc 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt
@@ -17,7 +17,7 @@
 package androidx.compose.animation.core
 
 internal fun VectorizedAnimationSpec<AnimationVector1D>.at(time: Long): Float =
-    getValue(
+    getValueFromMillis(
         time,
         AnimationVector1D(0f),
         AnimationVector1D(1f),
@@ -31,7 +31,7 @@
     start: Number,
     end: Number,
     startVelocity: Number
-) = getValue(
+) = getValueFromMillis(
     playTime,
     AnimationVector1D(start.toFloat()),
     AnimationVector1D(end.toFloat()),
@@ -43,9 +43,79 @@
     start: Number,
     end: Number,
     startVelocity: Number
-) = getVelocity(
-    playTime,
+) = getVelocityFromNanos(
+    playTime * MillisToNanos,
     AnimationVector1D(start.toFloat()),
     AnimationVector1D(end.toFloat()),
     AnimationVector1D(startVelocity.toFloat())
 ).value
+
+/**
+ * Returns the value of the animation at the given play time.
+ *
+ * @param playTimeMillis the play time that is used to determine the value of the animation.
+ */
+internal fun <T> Animation<T, *>.getValueFromMillis(playTimeMillis: Long): T =
+    getValueFromNanos(playTimeMillis * MillisToNanos)
+
+/**
+ * Returns the velocity (in [AnimationVector] form) of the animation at the given play time.
+ *
+ * @param playTimeMillis the play time that is used to calculate the velocity of the animation.
+ */
+internal fun <V : AnimationVector> Animation<*, V>.getVelocityVectorFromMillis(
+    playTimeMillis: Long
+): V = getVelocityVectorFromNanos(playTimeMillis * MillisToNanos)
+
+/**
+ * Returns whether the animation is finished at the given play time.
+ *
+ * @param playTimeMillis the play time used to determine whether the animation is finished.
+ */
+internal fun Animation<*, *>.isFinishedFromMillis(playTimeMillis: Long): Boolean {
+    return playTimeMillis >= durationMillis
+}
+
+internal fun <T, V : AnimationVector> Animation<T, V>.getVelocityFromMillis(
+    playTimeMillis: Long
+): T = typeConverter.convertFromVector(getVelocityVectorFromMillis(playTimeMillis))
+
+internal fun FloatAnimationSpec.getDurationMillis(
+    start: Float,
+    end: Float,
+    startVelocity: Float
+): Long = getDurationNanos(start, end, startVelocity) / MillisToNanos
+
+/**
+ * Calculates the value of the animation at given the playtime, with the provided start/end
+ * values, and start velocity.
+ *
+ * @param playTimeMillis time since the start of the animation
+ * @param start start value of the animation
+ * @param end end value of the animation
+ * @param startVelocity start velocity of the animation
+ */
+// TODO: bring all tests on to `getValueFromNanos`
+internal fun FloatAnimationSpec.getValueFromMillis(
+    playTimeMillis: Long,
+    start: Float,
+    end: Float,
+    startVelocity: Float
+): Float = getValueFromNanos(playTimeMillis * MillisToNanos, start, end, startVelocity)
+
+/**
+ * Calculates the velocity of the animation at given the playtime, with the provided start/end
+ * values, and start velocity.
+ *
+ * @param playTimeMillis time since the start of the animation
+ * @param start start value of the animation
+ * @param end end value of the animation
+ * @param startVelocity start velocity of the animation
+ */
+// TODO: bring all tests on to `getVelocityFromNanos`
+internal fun FloatAnimationSpec.getVelocityFromMillis(
+    playTimeMillis: Long,
+    start: Float,
+    end: Float,
+    startVelocity: Float
+): Float = getVelocityFromNanos(playTimeMillis * MillisToNanos, start, end, startVelocity)
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt
index 07af20a..93c9d99 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt
@@ -35,29 +35,34 @@
 
         val animWrapper = anim.createAnimation(startValue, startVelocity)
         // Obtain finish value by passing in an absurdly large playtime.
-        val finishValue = animWrapper.getValue(Int.MAX_VALUE.toLong())
-        val finishTime = animWrapper.durationMillis
+        val finishValue = animWrapper.getValueFromNanos(Int.MAX_VALUE.toLong())
+        val finishTimeNanos = animWrapper.durationMillis * MillisToNanos
 
-        for (playTime in 0L..4000L step 200L) {
-            val value = anim.getValue(playTime, startValue, startVelocity)
-            val velocity = anim.getVelocity(playTime, startValue, startVelocity)
-            val finished = playTime >= finishTime
-            assertTrue(finished == animWrapper.isFinished(playTime))
+        for (playTimeMillis in 0L..4000L step 200L) {
+            val playTimeNanos = playTimeMillis * MillisToNanos
+            val value = anim.getValueFromNanos(playTimeNanos, startValue, startVelocity)
+            val velocity = anim.getVelocityFromNanos(playTimeNanos, startValue, startVelocity)
+            val finished = playTimeNanos >= finishTimeNanos
+            assertTrue(finished == animWrapper.isFinishedFromNanos(playTimeNanos))
 
             if (!finished) {
                 // Before the animation finishes, absolute velocity is above the threshold
                 assertTrue(Math.abs(velocity) >= 2.0f)
-                assertEquals(value, animWrapper.getValue(playTime), epsilon)
-                assertEquals(velocity, animWrapper.getVelocityVector(playTime).value, epsilon)
-                assertTrue(playTime < finishTime)
+                assertEquals(value, animWrapper.getValueFromNanos(playTimeNanos), epsilon)
+                assertEquals(
+                    velocity,
+                    animWrapper.getVelocityVectorFromNanos(playTimeNanos).value,
+                    epsilon
+                )
+                assertTrue(playTimeNanos < finishTimeNanos)
             } else {
                 // When the animation is finished, expect absolute velocity < threshold
                 assertTrue(Math.abs(velocity) < 2.0f)
 
                 // Once the animation is finished, the value should not change any more
-                assertEquals(finishValue, animWrapper.getValue(playTime), epsilon)
+                assertEquals(finishValue, animWrapper.getValueFromNanos(playTimeNanos), epsilon)
 
-                assertTrue(playTime >= finishTime)
+                assertTrue(playTimeNanos >= finishTimeNanos)
             }
         }
     }
@@ -80,20 +85,20 @@
             startVelocity
         )
 
-        val finishValue = fullAnim.getValue(Int.MAX_VALUE.toLong())
+        val finishValue = fullAnim.getValueFromNanos(Int.MAX_VALUE.toLong())
 
         val finishValue1 = anim1.createAnimation(startValue, startVelocity)
-            .getValue(Int.MAX_VALUE.toLong())
+            .getValueFromNanos(Int.MAX_VALUE.toLong())
 
         val finishVelocity1 = anim1.createAnimation(startValue, startVelocity)
-            .getVelocityVector(Int.MAX_VALUE.toLong()).value
+            .getVelocityVectorFromNanos(Int.MAX_VALUE.toLong()).value
 
         // Verify that the finish velocity is at the threshold
         assertEquals(threshold, finishVelocity1, epsilon)
 
         // Feed in the finish value from anim1 to anim2
         val finishValue2 = anim2.createAnimation(finishValue1, finishVelocity1)
-            .getValue(Int.MAX_VALUE.toLong())
+            .getValueFromNanos(Int.MAX_VALUE.toLong())
 
         assertEquals(finishValue, finishValue2, 2f)
     }
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt
index 67d848c..afbdcf3 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt
@@ -131,7 +131,7 @@
             }
             assertEquals(
                 AnimationVector(v1, v2),
-                animation.getValue(
+                animation.getValueFromMillis(
                     time.toLong(), start, end,
                     AnimationVector(0f, 0f)
                 )
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt
index 1ad5905..0f93629 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt
@@ -33,7 +33,7 @@
         val end = 500f
         val playTime = 150L
 
-        val velocity = animation.getVelocity(playTime, start, end, 0f)
+        val velocity = animation.getVelocityFromMillis(playTime, start, end, 0f)
         val expectedVelocity = animation.toSpring(end).updateValues(start, 0f, playTime).velocity
         assertThat(velocity).isEqualTo(expectedVelocity)
     }
@@ -48,7 +48,7 @@
             spring(), Int.VectorConverter, start, end, 0
         )
 
-        val velocity = animation.getVelocity(playTime)
+        val velocity = animation.getVelocityFromMillis(playTime)
 
         val expectedVelocity = FloatSpringSpec().toSpring(end)
             .updateValues(start.toFloat(), 0f, playTime).velocity.toInt()
@@ -67,8 +67,8 @@
             spring(), Float.VectorConverter, start1, end1, 0f
         )
 
-        val interruptionValue = animation.getValue(interruptionTime)
-        val interruptionVelocity = animation.getVelocity(interruptionTime)
+        val interruptionValue = animation.getValueFromMillis(interruptionTime)
+        val interruptionVelocity = animation.getVelocityFromMillis(interruptionTime)
 
         // second animation will go from interruptionValue to interruptionValue with
         // applying the velocity from the first interrupted animation.
@@ -81,8 +81,8 @@
         )
         // let's verify values after 15 ms of the second animation
         val playTime = 15L
-        val resultValue = animation2.getValue(playTime)
-        val resultVelocity = animation2.getVelocity(playTime)
+        val resultValue = animation2.getValueFromMillis(playTime)
+        val resultVelocity = animation2.getVelocityFromMillis(playTime)
 
         val motion = FloatSpringSpec().toSpring(end2).updateValues(
             start2,
@@ -204,8 +204,8 @@
             100f,
             0f
         ).also { animation ->
-            assertEquals(0f, animation.getVelocityVector(animation.durationMillis).value)
-            assertEquals(100f, animation.getValue(animation.durationMillis))
+            assertEquals(0f, animation.getVelocityVectorFromMillis(animation.durationMillis).value)
+            assertEquals(100f, animation.getValueFromMillis(animation.durationMillis))
         }
     }
 
@@ -268,25 +268,25 @@
         )
 
         for (time in 0L until duration) {
-            val springVector = springVectorAnimation.getValue(
+            val springVector = springVectorAnimation.getValueFromMillis(
                 time,
                 AnimationVector(100f, 100f, 100f),
                 AnimationVector(0f, 0f, 0f),
                 AnimationVector(0f, 0f, 0f)
             )
-            val float1 = floatAnimation1.getValue(
+            val float1 = floatAnimation1.getValueFromMillis(
                 time,
                 AnimationVector(100f),
                 AnimationVector(0f),
                 AnimationVector(0f)
             )
-            val float2 = floatAnimation2.getValue(
+            val float2 = floatAnimation2.getValueFromMillis(
                 time,
                 AnimationVector(100f),
                 AnimationVector(0f),
                 AnimationVector(0f)
             )
-            val float3 = floatAnimation3.getValue(
+            val float3 = floatAnimation3.getValueFromMillis(
                 time,
                 AnimationVector(100f),
                 AnimationVector(0f),
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
index b6873c4..30ccaf0 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
@@ -51,8 +51,8 @@
         assertThat(repeat.at(Duration + 1)).isLessThan(0.1f)
         assertThat(repeat.at(Duration * 2 - 1)).isGreaterThan(0.9f)
         assertThat(repeat.at(Duration * 2)).isEqualTo(1f)
-        assertThat(animationWrapper.isFinished(Duration * 2L - 1L)).isFalse()
-        assertThat(animationWrapper.isFinished(Duration * 2L)).isTrue()
+        assertThat(animationWrapper.isFinishedFromMillis(Duration * 2L - 1L)).isFalse()
+        assertThat(animationWrapper.isFinishedFromMillis(Duration * 2L)).isTrue()
     }
 
     @Test
@@ -90,18 +90,18 @@
         )
 
         for (playtime in 0..100L) {
-            assertEquals(playtime.toFloat(), repeatAnim.getValue(playtime), 0.01f)
+            assertEquals(playtime.toFloat(), repeatAnim.getValueFromMillis(playtime), 0.01f)
         }
         for (playtime in 100..200L) {
-            assertEquals(200f - playtime.toFloat(), repeatAnim.getValue(playtime), 0.01f)
+            assertEquals(200f - playtime.toFloat(), repeatAnim.getValueFromMillis(playtime), 0.01f)
         }
-        assertEquals(100f, repeatAnim.getValue(100))
-        assertEquals(99f, repeatAnim.getValue(101))
-        assertEquals(0f, repeatAnim.getValue(200))
-        assertEquals(100f, repeatAnim.getValue(300))
-        assertEquals(80f, repeatAnim.getValue(880))
-        assertEquals(100f, repeatAnim.getValue(900))
-        assertEquals(100f, repeatAnim.getValue(901))
+        assertEquals(100f, repeatAnim.getValueFromMillis(100))
+        assertEquals(99f, repeatAnim.getValueFromMillis(101))
+        assertEquals(0f, repeatAnim.getValueFromMillis(200))
+        assertEquals(100f, repeatAnim.getValueFromMillis(300))
+        assertEquals(80f, repeatAnim.getValueFromMillis(880))
+        assertEquals(100f, repeatAnim.getValueFromMillis(900))
+        assertEquals(100f, repeatAnim.getValueFromMillis(901))
     }
 
     @Test
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt
index eb2f71d..8c1102e 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt
@@ -35,6 +35,6 @@
         )
 
         assertThat(animation.at(0)).isEqualTo(1f)
-        assertThat(animationWrapper.isFinished(0)).isTrue()
+        assertThat(animationWrapper.isFinishedFromMillis(0)).isTrue()
     }
 }
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt
index a734bd1..72c6567 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt
@@ -51,8 +51,8 @@
                     0f, 1f, 0f,
                     spring(dampingRatio = Spring.DampingRatioMediumBouncy)
                 ) { value, velocity ->
-                    assertEquals(anim.getValue(playTimeMillis), value, 0.001f)
-                    assertEquals(anim.getVelocity(playTimeMillis), velocity, 0.001f)
+                    assertEquals(anim.getValueFromMillis(playTimeMillis), value, 0.001f)
+                    assertEquals(anim.getVelocityFromMillis(playTimeMillis), velocity, 0.001f)
                     playTimeMillis += interval
                 }
             }
@@ -84,7 +84,7 @@
                     from, to,
                     animationSpec = tween(500)
                 ) { value, _ ->
-                    val expectedValue = anim.getValue(playTimeMillis)
+                    val expectedValue = anim.getValueFromMillis(playTimeMillis)
                     assertEquals(expectedValue.x, value.x, 0.001f)
                     assertEquals(expectedValue.y, value.y, 0.001f)
                     playTimeMillis += interval
@@ -113,8 +113,8 @@
                     from, velocity,
                     animationSpec = FloatExponentialDecaySpec()
                 ) { value, velocity ->
-                    assertEquals(anim.getValue(playTimeMillis), value, 0.001f)
-                    assertEquals(anim.getVelocity(playTimeMillis), velocity, 0.001f)
+                    assertEquals(anim.getValueFromMillis(playTimeMillis), value, 0.001f)
+                    assertEquals(anim.getVelocityFromMillis(playTimeMillis), velocity, 0.001f)
                     playTimeMillis += interval
                 }
             }
@@ -154,7 +154,7 @@
                 ) {
                     assertTrue(animationState.isRunning)
                     assertTrue(isRunning)
-                    val expectedValue = anim.getValue(playTimeMillis)
+                    val expectedValue = anim.getValueFromMillis(playTimeMillis)
                     assertEquals(expectedValue.x, value.x, 0.001f)
                     assertEquals(expectedValue.y, value.y, 0.001f)
                     if (playTimeMillis == 0L) {
@@ -201,8 +201,8 @@
                 state.animateDecay(
                     FloatExponentialDecaySpec().generateDecayAnimationSpec()
                 ) {
-                    assertEquals(anim.getValue(playTimeMillis), value, 0.001f)
-                    assertEquals(anim.getVelocity(playTimeMillis), velocity, 0.001f)
+                    assertEquals(anim.getValueFromMillis(playTimeMillis), value, 0.001f)
+                    assertEquals(anim.getVelocityFromMillis(playTimeMillis), velocity, 0.001f)
                     playTimeMillis += interval
                     assertEquals(value, state.value, 0.0001f)
                     assertEquals(velocity, state.velocity, 0.0001f)
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt
index ff7416e..a1666cb 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt
@@ -38,7 +38,7 @@
         )
 
         fun atPlaytime(playTime: Long) =
-            animation.getValue(playTime, start, end, AnimationVector1D(0f)).value
+            animation.getValueFromMillis(playTime, start, end, AnimationVector1D(0f)).value
 
         assertThat(atPlaytime(0L)).isZero()
         assertThat(atPlaytime(testDelay / 2)).isZero()
diff --git a/compose/animation/animation/api/current.txt b/compose/animation/animation/api/current.txt
index 83672d1..8750af5 100644
--- a/compose/animation/animation/api/current.txt
+++ b/compose/animation/animation/api/current.txt
@@ -36,7 +36,7 @@
   }
 
   public final class CrossfadeKt {
-    method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? current, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animation, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
   }
 
   public final class DisposableAnimationClock implements androidx.compose.animation.core.AnimationClockObservable {
diff --git a/compose/animation/animation/api/public_plus_experimental_current.txt b/compose/animation/animation/api/public_plus_experimental_current.txt
index 83672d1..8750af5 100644
--- a/compose/animation/animation/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation/api/public_plus_experimental_current.txt
@@ -36,7 +36,7 @@
   }
 
   public final class CrossfadeKt {
-    method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? current, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animation, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
   }
 
   public final class DisposableAnimationClock implements androidx.compose.animation.core.AnimationClockObservable {
diff --git a/compose/animation/animation/api/restricted_current.txt b/compose/animation/animation/api/restricted_current.txt
index 83672d1..8750af5 100644
--- a/compose/animation/animation/api/restricted_current.txt
+++ b/compose/animation/animation/api/restricted_current.txt
@@ -36,7 +36,7 @@
   }
 
   public final class CrossfadeKt {
-    method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? current, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animation, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
   }
 
   public final class DisposableAnimationClock implements androidx.compose.animation.core.AnimationClockObservable {
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt
index a1547ec..16f7699 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt
@@ -27,9 +27,9 @@
 import androidx.compose.foundation.layout.preferredHeight
 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.saveable.rememberSaveableStateHolder
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -39,7 +39,7 @@
 
 @Composable
 fun CrossfadeDemo() {
-    var current by savedInstanceState { 0 }
+    var current by rememberSaveable { mutableStateOf(0) }
     Column {
         Row {
             tabs.forEachIndexed { index, tab ->
@@ -57,7 +57,7 @@
             }
         }
         val saveableStateHolder = rememberSaveableStateHolder()
-        Crossfade(current = current) { current ->
+        Crossfade(targetState = current) { current ->
             saveableStateHolder.SaveableStateProvider(current) {
                 val tab = tabs[current]
                 arrayOf<Any?>()
diff --git a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt
index 2455d96..abf7f63 100644
--- a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt
+++ b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt
@@ -24,7 +24,7 @@
 @Sampled
 @Composable
 fun CrossfadeSample() {
-    Crossfade(current = "A") { screen ->
+    Crossfade(targetState = "A") { screen ->
         when (screen) {
             "A" -> Text("Page A")
             "B" -> Text("Page B")
diff --git a/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
index daf5be0..a73246b 100644
--- a/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
+++ b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
@@ -97,7 +97,7 @@
         rule.setContent {
             Crossfade(
                 showFirst,
-                animation = TweenSpec(durationMillis = duration)
+                animationSpec = TweenSpec(durationMillis = duration)
             ) {
                 BasicText(if (it) First else Second)
                 DisposableEffect(Unit) {
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
index ec97c0c..298142e 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
@@ -14,20 +14,19 @@
  * limitations under the License.
  */
 
-@file:Suppress("DEPRECATION")
-
 package androidx.compose.animation
 
-import androidx.compose.animation.core.AnimatedFloat
-import androidx.compose.animation.core.AnimationEndReason
 import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.animation.core.animateFloat
 import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.updateTransition
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.RecomposeScope
-import androidx.compose.runtime.currentRecomposeScope
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
-import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.alpha
@@ -38,92 +37,57 @@
  *
  * @sample androidx.compose.animation.samples.CrossfadeSample
  *
- * @param current is a key representing your current layout state. every time you change a key
+ * @param targetState is a key representing your target layout state. Every time you change a key
  * the animation will be triggered. The [content] called with the old key will be faded out while
  * the [content] called with the new key will be faded in.
  * @param modifier Modifier to be applied to the animation container.
- * @param animation the [AnimationSpec] to configure the animation.
+ * @param animationSpec the [AnimationSpec] to configure the animation.
  */
 @Composable
 fun <T> Crossfade(
-    current: T,
+    targetState: T,
     modifier: Modifier = Modifier,
-    animation: AnimationSpec<Float> = tween(),
+    animationSpec: FiniteAnimationSpec<Float> = tween(),
     content: @Composable (T) -> Unit
 ) {
-    val state = remember { CrossfadeState<T>() }
-    if (current != state.current) {
-        state.current = current
-        val keys = state.items.map { it.key }.toMutableList()
-        if (!keys.contains(current)) {
-            keys.add(current)
+    val items = remember { mutableStateListOf<CrossfadeAnimationItem<T>>() }
+    val transitionState = remember { MutableTransitionState(targetState) }
+    val targetChanged = (targetState != transitionState.targetState)
+    transitionState.targetState = targetState
+    val transition = updateTransition(transitionState)
+    if (targetChanged || items.isEmpty()) {
+        // Only manipulate the list when the state is changed, or in the first run.
+        val keys = items.map { it.key }.run {
+            if (!contains(targetState)) {
+                toMutableList().also { it.add(targetState) }
+            } else {
+                this
+            }
         }
-        state.items.clear()
-        keys.mapTo(state.items) { key ->
-            CrossfadeAnimationItem(key) { children ->
-                val alpha = animatedAlpha(
-                    animation = animation,
-                    visible = key == current,
-                    onAnimationFinish = {
-                        if (key == state.current) {
-                            // leave only the current in the list
-                            state.items.removeAll { it.key != state.current }
-                            state.scope?.invalidate()
-                        }
+        items.clear()
+        keys.mapTo(items) { key ->
+            CrossfadeAnimationItem(key) {
+                key(key) {
+                    val alpha by transition.animateFloat(
+                        transitionSpec = { animationSpec }
+                    ) { if (it == key) 1f else 0f }
+                    Box(Modifier.alpha(alpha = alpha)) {
+                        content(key)
                     }
-                )
-                Box(Modifier.alpha(alpha.value)) {
-                    children()
                 }
             }
         }
+    } else if (transitionState.currentState == transitionState.targetState) {
+        // Remove all the intermediate items from the list once the animation is finished.
+        items.removeAll { it.key != transitionState.targetState }
     }
-    Box(modifier) {
-        state.scope = currentRecomposeScope
-        state.items.fastForEach { (item, alpha) ->
-            key(item) {
-                alpha {
-                    content(item)
-                }
-            }
-        }
-    }
-}
 
-private class CrossfadeState<T> {
-    // we use Any here as something which will not be equals to the real initial value
-    var current: Any? = Any()
-    var items = mutableListOf<CrossfadeAnimationItem<T>>()
-    var scope: RecomposeScope? = null
+    Box(modifier) {
+        items.fastForEach { it.content() }
+    }
 }
 
 private data class CrossfadeAnimationItem<T>(
     val key: T,
-    val transition: CrossfadeTransition
+    val content: @Composable () -> Unit
 )
-
-private typealias CrossfadeTransition = @Composable (content: @Composable () -> Unit) -> Unit
-
-@Composable
-private fun animatedAlpha(
-    animation: AnimationSpec<Float>,
-    visible: Boolean,
-    onAnimationFinish: () -> Unit = {}
-): AnimatedFloat {
-    val animatedFloat = animatedFloat(if (!visible) 1f else 0f)
-    DisposableEffect(visible) {
-        animatedFloat.animateTo(
-            if (visible) 1f else 0f,
-            anim = animation,
-            onEnd = { reason, _ ->
-                if (reason == AnimationEndReason.TargetReached) {
-                    onAnimationFinish()
-                }
-            }
-        )
-        onDispose {
-            animatedFloat.stop()
-        }
-    }
-    return animatedFloat
-}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
index 6dcaf71..b022954 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
@@ -220,13 +220,16 @@
             ) {
                 "${it.groupValues[1]}<>"
             }
-            // composableLambdaInstance(<>, true)
+            // composableLambdaInstance(<>, true, )
             .replace(
                 Regex(
-                    "(composableLambdaInstance\\()([-\\d]+)"
+                    "(composableLambdaInstance\\()([-\\d]+, (true|false), (null|\"(.*)\")\\))"
                 )
             ) {
-                "${it.groupValues[1]}<>"
+                val callStart = it.groupValues[1]
+                val tracked = it.groupValues[3]
+                val sourceInfo = it.groupValues[5]
+                "$callStart<>, $tracked, \"${generateSourceInfo(sourceInfo, source)}\")"
             }
             // composableLambda(%composer, <>, true)
             .replace(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
index 90d06fb..834c976 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
@@ -753,7 +753,13 @@
             @Composable
             fun C(items: List<String>, %composer: Composer?, %changed: Int) {
               %composer.startRestartGroup(<>, "C(C)<X(item...>:Test.kt")
-              X(items, composableLambda(%composer, <>, true, "C<A(item...>,<A(Wrap...>:Test.kt") { item: String, %composer: Composer?, %changed: Int ->
+              X(items, ComposableSingletons%TestKt.lambda-1, %composer, 0b1000)
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                C(items, %composer, %changed or 0b0001)
+              }
+            }
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function3<String, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<A(item...>,<A(Wrap...>:Test.kt") { item: String, %composer: Composer?, %changed: Int ->
                 val %dirty = %changed
                 if (%changed and 0b1110 === 0) {
                   %dirty = %dirty or if (%composer.changed(item)) 0b0100 else 0b0010
@@ -764,9 +770,6 @@
                 } else {
                   %composer.skipToGroupEnd()
                 }
-              }, %composer, 0b00111000)
-              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                C(items, %composer, %changed or 0b0001)
               }
             }
         """
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
index 73b71ef..4591ad7 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
@@ -42,11 +42,27 @@
         // and one for the content lambda passed into Foo. Importantly, there is no lambda for
         // the content lambda's restart group because we are using the lambda itself.
         """
+            public final class ComposableSingletons%TestKt {
+              public final static LComposableSingletons%TestKt; INSTANCE
+              public <init>()V
+              public static Lkotlin/jvm/functions/Function2; lambda-1
+              public final getLambda-1%test_module()Lkotlin/jvm/functions/Function2;
+              final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+              static <clinit>()V
+            }
+            final class ComposableSingletons%TestKt%lambda-1%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+              <init>()V
+              public final invoke(Landroidx/compose/runtime/Composer;I)V
+              public final static LComposableSingletons%TestKt%lambda-1%1; INSTANCE
+              public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+              static <clinit>()V
+              final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+              OUTERCLASS ComposableSingletons%TestKt <clinit> ()V
+            }
             public final class TestKt {
               final static INNERCLASS TestKt%Foo%1 null null
               public final static Foo(Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
               final static INNERCLASS TestKt%Bar%1 null null
-              final static INNERCLASS TestKt%Bar%2 null null
               public final static Bar(Landroidx/compose/runtime/Composer;I)V
             }
             final class TestKt%Foo%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
@@ -59,20 +75,11 @@
               OUTERCLASS TestKt Foo (Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
             }
             final class TestKt%Bar%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
-              <init>()V
-              public final invoke(Landroidx/compose/runtime/Composer;I)V
-              public final static LTestKt%Bar%1; INSTANCE
-              public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-              static <clinit>()V
-              final static INNERCLASS TestKt%Bar%1 null null
-              OUTERCLASS TestKt Bar (Landroidx/compose/runtime/Composer;I)V
-            }
-            final class TestKt%Bar%2 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
               <init>(I)V
               public final invoke(Landroidx/compose/runtime/Composer;I)V
               final synthetic I %%changed
               public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-              final static INNERCLASS TestKt%Bar%2 null null
+              final static INNERCLASS TestKt%Bar%1 null null
               OUTERCLASS TestKt Bar (Landroidx/compose/runtime/Composer;I)V
             }
         """
@@ -636,12 +643,28 @@
             }
         """,
         """
+            public final class ComposableSingletons%TestKt {
+              public final static LComposableSingletons%TestKt; INSTANCE
+              public <init>()V
+              public static Lkotlin/jvm/functions/Function3; lambda-1
+              public final getLambda-1%test_module()Lkotlin/jvm/functions/Function3;
+              final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+              static <clinit>()V
+            }
+            final class ComposableSingletons%TestKt%lambda-1%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function3 {
+              <init>()V
+              public final invoke(ILandroidx/compose/runtime/Composer;I)V
+              public final static LComposableSingletons%TestKt%lambda-1%1; INSTANCE
+              public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+              static <clinit>()V
+              final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+              OUTERCLASS ComposableSingletons%TestKt <clinit> ()V
+            }
             public final class TestKt {
               private final static Lkotlin/jvm/functions/Function3; foo
               public final static getFoo()Lkotlin/jvm/functions/Function3;
               final static INNERCLASS TestKt%Bar%1 null null
               public final static Bar(Landroidx/compose/runtime/Composer;I)V
-              final static INNERCLASS TestKt%foo%1 null null
               static <clinit>()V
             }
             final class TestKt%Bar%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
@@ -652,15 +675,6 @@
               final static INNERCLASS TestKt%Bar%1 null null
               OUTERCLASS TestKt Bar (Landroidx/compose/runtime/Composer;I)V
             }
-            final class TestKt%foo%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function3 {
-              <init>()V
-              public final invoke(ILandroidx/compose/runtime/Composer;I)V
-              public final static LTestKt%foo%1; INSTANCE
-              public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-              static <clinit>()V
-              final static INNERCLASS TestKt%foo%1 null null
-              OUTERCLASS TestKt <clinit> ()V
-            }
         """
     )
 
@@ -674,19 +688,26 @@
             }
         """,
         """
-            public final class TestKt {
-              final static INNERCLASS TestKt%Bar%foo%1 null null
-              final static INNERCLASS TestKt%Bar%1 null null
-              public final static Bar(Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
+            public final class ComposableSingletons%TestKt {
+              public final static LComposableSingletons%TestKt; INSTANCE
+              public <init>()V
+              public static Lkotlin/jvm/functions/Function3; lambda-1
+              public final getLambda-1%test_module()Lkotlin/jvm/functions/Function3;
+              final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+              static <clinit>()V
             }
-            final class TestKt%Bar%foo%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function3 {
+            final class ComposableSingletons%TestKt%lambda-1%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function3 {
               <init>()V
               public final invoke(ILandroidx/compose/runtime/Composer;I)V
-              public final static LTestKt%Bar%foo%1; INSTANCE
+              public final static LComposableSingletons%TestKt%lambda-1%1; INSTANCE
               public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
               static <clinit>()V
-              final static INNERCLASS TestKt%Bar%foo%1 null null
-              OUTERCLASS TestKt Bar (Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
+              final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+              OUTERCLASS ComposableSingletons%TestKt <clinit> ()V
+            }
+            public final class TestKt {
+              final static INNERCLASS TestKt%Bar%1 null null
+              public final static Bar(Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
             }
             final class TestKt%Bar%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
               <init>(Lkotlin/jvm/functions/Function2;I)V
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
index 68df589..99b592d 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
@@ -2311,13 +2311,7 @@
             fun Test(%composer: Composer?, %changed: Int) {
               %composer.startRestartGroup(<>, "C(Test)<W>:Test.kt")
               if (%changed !== 0 || !%composer.skipping) {
-                W(composableLambda(%composer, <>, true, "C<A()>:Test.kt") { %composer: Composer?, %changed: Int ->
-                  if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
-                    A(%composer, 0)
-                  } else {
-                    %composer.skipToGroupEnd()
-                  }
-                }, %composer, 0b0110)
+                W(ComposableSingletons%TestKt.lambda-1, %composer, 0)
               } else {
                 %composer.skipToGroupEnd()
               }
@@ -2325,6 +2319,15 @@
                 Test(%composer, %changed or 0b0001)
               }
             }
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<A()>:Test.kt") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  A(%composer, 0)
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
         """
     )
 
@@ -2382,22 +2385,7 @@
             fun Test(%composer: Composer?, %changed: Int) {
               %composer.startRestartGroup(<>, "C(Test)<Wrap>:Test.kt")
               if (%changed !== 0 || !%composer.skipping) {
-                Wrap(composableLambda(%composer, <>, true, "C<effect>:Test.kt") { %composer: Composer?, %changed: Int ->
-                  if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
-                    %composer.startReplaceableGroup(<>, "*<effect>")
-                    repeat(number) { it: Int ->
-                      effects[it] = effect({
-                        0
-                      }, %composer, 0)
-                    }
-                    %composer.endReplaceableGroup()
-                    outside = effect({
-                      "0"
-                    }, %composer, 0)
-                  } else {
-                    %composer.skipToGroupEnd()
-                  }
-                }, %composer, 0b0110)
+                Wrap(ComposableSingletons%TestKt.lambda-1, %composer, 0)
               } else {
                 %composer.skipToGroupEnd()
               }
@@ -2405,6 +2393,24 @@
                 Test(%composer, %changed or 0b0001)
               }
             }
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<effect>:Test.kt") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  %composer.startReplaceableGroup(<>, "*<effect>")
+                  repeat(number) { it: Int ->
+                    effects[it] = effect({
+                      0
+                    }, %composer, 0)
+                  }
+                  %composer.endReplaceableGroup()
+                  outside = effect({
+                    "0"
+                  }, %composer, 0)
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
         """,
         """
             import androidx.compose.runtime.Composable
@@ -3158,27 +3164,7 @@
             fun Test(%composer: Composer?, %changed: Int) {
               %composer.startRestartGroup(<>, "C(Test)<W>:Test.kt")
               if (%changed !== 0 || !%composer.skipping) {
-                W(composableLambda(%composer, <>, true, "C<IW>:Test.kt") { %composer: Composer?, %changed: Int ->
-                  if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
-                    IW({ %composer: Composer?, %changed: Int ->
-                      %composer.startReplaceableGroup(<>, "C<T(2)>,<T(4)>:Test.kt")
-                      if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
-                        T(2, %composer, 0b0110)
-                        %composer.startReplaceableGroup(<>, "*<T(3)>")
-                        repeat(3) { it: Int ->
-                          T(3, %composer, 0b0110)
-                        }
-                        %composer.endReplaceableGroup()
-                        T(4, %composer, 0b0110)
-                      } else {
-                        %composer.skipToGroupEnd()
-                      }
-                      %composer.endReplaceableGroup()
-                    }, %composer, 0)
-                  } else {
-                    %composer.skipToGroupEnd()
-                  }
-                }, %composer, 0b0110)
+                W(ComposableSingletons%TestKt.lambda-1, %composer, 0)
               } else {
                 %composer.skipToGroupEnd()
               }
@@ -3186,6 +3172,29 @@
                 Test(%composer, %changed or 0b0001)
               }
             }
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<IW>:Test.kt") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  IW({ %composer: Composer?, %changed: Int ->
+                    %composer.startReplaceableGroup(<>, "C<T(2)>,<T(4)>:Test.kt")
+                    if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                      T(2, %composer, 0b0110)
+                      %composer.startReplaceableGroup(<>, "*<T(3)>")
+                      repeat(3) { it: Int ->
+                        T(3, %composer, 0b0110)
+                      }
+                      %composer.endReplaceableGroup()
+                      T(4, %composer, 0b0110)
+                    } else {
+                      %composer.skipToGroupEnd()
+                    }
+                    %composer.endReplaceableGroup()
+                  }, %composer, 0)
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
         """
     )
 }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
index 4ec08fe..31c193f 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
@@ -88,13 +88,7 @@
             fun Test(%composer: Composer?, %changed: Int) {
               %composer.startRestartGroup(<>, "C(Test)")
               if (%changed !== 0 || !%composer.skipping) {
-                W(composableLambda(%composer, <>, true, null) { %composer: Composer?, %changed: Int ->
-                  if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
-                    A(%composer, 0)
-                  } else {
-                    %composer.skipToGroupEnd()
-                  }
-                }, %composer, 0b0110)
+                W(ComposableSingletons%TestKt.lambda-1, %composer, 0)
               } else {
                 %composer.skipToGroupEnd()
               }
@@ -102,6 +96,15 @@
                 Test(%composer, %changed or 0b0001)
               }
             }
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  A(%composer, 0)
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
         """
     )
 
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
index fda6d65..8dcd32f0 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
@@ -693,22 +693,24 @@
               } else if (%changed and 0b1110 === 0) {
                 %dirty = %dirty or if (%composer.changed(modifier)) 0b0100 else 0b0010
               }
-              if (%default and 0b0010 !== 0) {
-                %dirty = %dirty or 0b00110000
-              } else if (%changed and 0b01110000 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b00100000 else 0b00010000
+              if (%changed and 0b01110000 === 0) {
+                %dirty = %dirty or if (%default and 0b0010 === 0 && %composer.changed(content)) 0b00100000 else 0b00010000
               }
               if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
-                if (%default and 0b0001 !== 0) {
-                  modifier = Companion
-                }
-                if (%default and 0b0010 !== 0) {
-                  content = composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer?, %changed: Int ->
-                    if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
-                      Unit
-                    } else {
-                      %composer.skipToGroupEnd()
-                    }
+                if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) {
+                  %composer.startDefaults()
+                  if (%default and 0b0001 !== 0) {
+                    modifier = Companion
+                  }
+                  if (%default and 0b0010 !== 0) {
+                    content = ComposableSingletons%TestKt.lambda-1
+                    %dirty = %dirty and 0b01110000.inv()
+                  }
+                  %composer.endDefaults()
+                } else {
+                  %composer.skipCurrentGroup()
+                  if (%default and 0b0010 !== 0) {
+                    %dirty = %dirty and 0b01110000.inv()
                   }
                 }
                 println()
@@ -719,6 +721,15 @@
                 SimpleBox(modifier, content, %composer, %changed or 0b0001, %default)
               }
             }
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
         """
     )
 
@@ -738,19 +749,22 @@
             }
         """,
         """
-            val foo: Function4<Int, Foo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { x: Int, y: Foo, %composer: Composer?, %changed: Int ->
-              val %dirty = %changed
-              if (%changed and 0b1110 === 0) {
-                %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
-              }
-              if (%changed and 0b01110000 === 0) {
-                %dirty = %dirty or if (%composer.changed(y)) 0b00100000 else 0b00010000
-              }
-              if (%dirty and 0b001011011011 xor 0b10010010 !== 0 || !%composer.skipping) {
-                A(x, %composer, 0b1110 and %dirty)
-                B(y, %composer, 0b1110 and %dirty shr 0b0011)
-              } else {
-                %composer.skipToGroupEnd()
+            val foo: Function4<Int, Foo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function4<Int, Foo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<A(x)>,<B(y)>:") { x: Int, y: Foo, %composer: Composer?, %changed: Int ->
+                val %dirty = %changed
+                if (%changed and 0b1110 === 0) {
+                  %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
+                }
+                if (%changed and 0b01110000 === 0) {
+                  %dirty = %dirty or if (%composer.changed(y)) 0b00100000 else 0b00010000
+                }
+                if (%dirty and 0b001011011011 xor 0b10010010 !== 0 || !%composer.skipping) {
+                  A(x, %composer, 0b1110 and %dirty)
+                  B(y, %composer, 0b1110 and %dirty shr 0b0011)
+                } else {
+                  %composer.skipToGroupEnd()
+                }
               }
             }
         """
@@ -770,9 +784,12 @@
             }
         """,
         """
-            val foo: Function4<Int, Foo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { x: Int, y: Foo, %composer: Composer?, %changed: Int ->
-              A(x, %composer, 0b1110 and %changed)
-              B(y, %composer, 0b1000)
+            val foo: Function4<Int, Foo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function4<Int, Foo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<A(x)>,<B(y)>:") { x: Int, y: Foo, %composer: Composer?, %changed: Int ->
+                A(x, %composer, 0b1110 and %changed)
+                B(y, %composer, 0b1000)
+              }
             }
         """
     )
@@ -811,13 +828,7 @@
             fun Example(%composer: Composer?, %changed: Int) {
               %composer.startRestartGroup(<>, "C(Example)<SomeTh...>:Test.kt")
               if (%changed !== 0 || !%composer.skipping) {
-                SomeThing(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer?, %changed: Int ->
-                  if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
-                    val id = object
-                  } else {
-                    %composer.skipToGroupEnd()
-                  }
-                }, %composer, 0b0110)
+                SomeThing(ComposableSingletons%TestKt.lambda-1, %composer, 0)
               } else {
                 %composer.skipToGroupEnd()
               }
@@ -825,6 +836,15 @@
                 Example(%composer, %changed or 0b0001)
               }
             }
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  val id = object
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
         """
     )
 
@@ -1156,15 +1176,18 @@
             }
         """,
         """
-            val test: Function3<Int, Composer, Int, Unit> = composableLambdaInstance(<>, true) { x: Int, %composer: Composer?, %changed: Int ->
-              val %dirty = %changed
-              if (%changed and 0b1110 === 0) {
-                %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
-              }
-              if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
-                A(x, 0, %composer, 0b1110 and %dirty, 0b0010)
-              } else {
-                %composer.skipToGroupEnd()
+            val test: Function3<Int, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function3<Int, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<A(x)>:") { x: Int, %composer: Composer?, %changed: Int ->
+                val %dirty = %changed
+                if (%changed and 0b1110 === 0) {
+                  %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
+                }
+                if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
+                  A(x, 0, %composer, 0b1110 and %dirty, 0b0010)
+                } else {
+                  %composer.skipToGroupEnd()
+                }
               }
             }
         """
@@ -1492,13 +1515,7 @@
               %composer.startRestartGroup(<>, "C(A)<D>,<C({})>,<C(stab...>,<C(16.d...>,<C(Dp(1...>,<C(16.d...>,<C(norm...>,<C(Int....>,<C(stab...>,<C(Modi...>,<C(Foo....>,<C(cons...>,<C(123)>,<C(123>,<C(x)>,<C(x>:Test.kt")
               if (%changed !== 0 || !%composer.skipping) {
                 val x = 123
-                D(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer?, %changed: Int ->
-                  if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
-                    Unit
-                  } else {
-                    %composer.skipToGroupEnd()
-                  }
-                }, %composer, 0b0110)
+                D(ComposableSingletons%TestKt.lambda-1, %composer, 0)
                 C({
                 }, %composer, 0)
                 C(stableFun(123), %composer, 0b0110)
@@ -1535,6 +1552,15 @@
                 B(%composer, %changed or 0b0001)
               }
             }
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
         """
     )
 
@@ -1553,13 +1579,7 @@
             fun Example(%composer: Composer?, %changed: Int) {
               %composer.startRestartGroup(<>, "C(Example)<D>:Test.kt")
               if (%changed !== 0 || !%composer.skipping) {
-                D(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer?, %changed: Int ->
-                  if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
-                    Unit
-                  } else {
-                    %composer.skipToGroupEnd()
-                  }
-                }, %composer, 0b0110)
+                D(ComposableSingletons%TestKt.lambda-1, %composer, 0)
               } else {
                 %composer.skipToGroupEnd()
               }
@@ -1567,7 +1587,15 @@
                 Example(%composer, %changed or 0b0001)
               }
             }
-
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
         """
     )
 
@@ -1895,37 +1923,43 @@
             }
         """,
         """
-            val unstableUnused: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
-              val %dirty = %changed
-              %dirty = %dirty or 0b0110
-              if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
-                Unit
-              } else {
-                %composer.skipToGroupEnd()
+            val unstableUnused: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+            val unstableUsed: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-2
+            val stableUnused: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-3
+            val stableUsed: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-4
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+                val %dirty = %changed
+                %dirty = %dirty or 0b0110
+                if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
               }
-            }
-            val unstableUsed: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
-              val %dirty = %changed
-              print(x)
-            }
-            val stableUnused: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
-              val %dirty = %changed
-              %dirty = %dirty or 0b0110
-              if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
-                Unit
-              } else {
-                %composer.skipToGroupEnd()
-              }
-            }
-            val stableUsed: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
-              val %dirty = %changed
-              if (%changed and 0b1110 === 0) {
-                %dirty = %dirty or if (%composer.changed(<this>)) 0b0100 else 0b0010
-              }
-              if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
+              val lambda-2: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+                val %dirty = %changed
                 print(x)
-              } else {
-                %composer.skipToGroupEnd()
+              }
+              val lambda-3: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+                val %dirty = %changed
+                %dirty = %dirty or 0b0110
+                if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+              val lambda-4: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+                val %dirty = %changed
+                if (%changed and 0b1110 === 0) {
+                  %dirty = %dirty or if (%composer.changed(<this>)) 0b0100 else 0b0010
+                }
+                if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
+                  print(x)
+                } else {
+                  %composer.skipToGroupEnd()
+                }
               }
             }
         """
@@ -2649,13 +2683,12 @@
         """
             import androidx.compose.ui.Modifier
             import androidx.compose.ui.unit.Dp
-            import androidx.compose.runtime.emptyContent
 
             @Composable
             fun Box2(
                 modifier: Modifier = Modifier,
                 paddingStart: Dp = Dp.Unspecified,
-                content: @Composable () -> Unit = emptyContent()
+                content: @Composable () -> Unit = {}
             ) {
 
             }
@@ -2676,10 +2709,8 @@
               if (%changed and 0b01110000 === 0) {
                 %dirty = %dirty or if (%default and 0b0010 === 0 && %composer.changed(paddingStart.value)) 0b00100000 else 0b00010000
               }
-              if (%default and 0b0100 !== 0) {
-                %dirty = %dirty or 0b000110000000
-              } else if (%changed and 0b001110000000 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b000100000000 else 0b10000000
+              if (%changed and 0b001110000000 === 0) {
+                %dirty = %dirty or if (%default and 0b0100 === 0 && %composer.changed(content)) 0b000100000000 else 0b10000000
               }
               if (%dirty and 0b001011011011 xor 0b10010010 !== 0 || !%composer.skipping) {
                 if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) {
@@ -2692,7 +2723,8 @@
                     %dirty = %dirty and 0b01110000.inv()
                   }
                   if (%default and 0b0100 !== 0) {
-                    content = emptyContent()
+                    content = ComposableSingletons%TestKt.lambda-1
+                    %dirty = %dirty and 0b001110000000.inv()
                   }
                   %composer.endDefaults()
                 } else {
@@ -2700,6 +2732,9 @@
                   if (%default and 0b0010 !== 0) {
                     %dirty = %dirty and 0b01110000.inv()
                   }
+                  if (%default and 0b0100 !== 0) {
+                    %dirty = %dirty and 0b001110000000.inv()
+                  }
                 }
               } else {
                 %composer.skipToGroupEnd()
@@ -2708,6 +2743,15 @@
                 Box2(modifier, paddingStart, content, %composer, %changed or 0b0001, %default)
               }
             }
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
         """
     )
 
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
index 9b11630..1e4199a 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
@@ -20,6 +20,273 @@
 
 class LambdaMemoizationTransformTests : ComposeIrTransformTest() {
 
+    @Test
+    fun testCapturedThisFromFieldInitializer(): Unit = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.Composable
+
+            class A {
+                val b = ""
+                val c = @Composable {
+                    print(b)
+                }
+            }
+        """,
+        """
+            @StabilityInferred(parameters = 0)
+            class A {
+              val b: String = ""
+              val c: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, true, "") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  print(b)
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+              static val %stable: Int = 0
+            }
+        """,
+        """
+        """
+    )
+
+    @Test
+    fun testLocalInALocal(): Unit = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.Composable
+
+            @Composable fun Example() {
+                @Composable fun A() { }
+                @Composable fun B(content: @Composable () -> Unit) { }
+                @Composable fun C() {
+                    B { A() }
+                }
+            }
+        """,
+        """
+            @Composable
+            fun Example(%composer: Composer?, %changed: Int) {
+              %composer.startRestartGroup(<>, "C(Example):Test.kt")
+              if (%changed !== 0 || !%composer.skipping) {
+                @Composable
+                fun A(%composer: Composer?, %changed: Int) {
+                  %composer.startRestartGroup(<>, "C(A):Test.kt")
+                  if (%changed !== 0 || !%composer.skipping) {
+                  } else {
+                    %composer.skipToGroupEnd()
+                  }
+                  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    A(%composer, %changed or 0b0001)
+                  }
+                }
+                @Composable
+                fun B(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
+                  %composer.startRestartGroup(<>, "C(B):Test.kt")
+                  val %dirty = %changed
+                  if (%changed and 0b1110 === 0) {
+                    %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+                  }
+                  if (%dirty and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  } else {
+                    %composer.skipToGroupEnd()
+                  }
+                  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    B(content, %composer, %changed or 0b0001)
+                  }
+                }
+                @Composable
+                fun C(%composer: Composer?, %changed: Int) {
+                  %composer.startRestartGroup(<>, "C(C)<B>:Test.kt")
+                  if (%changed !== 0 || !%composer.skipping) {
+                    B(composableLambda(%composer, <>, false, "C<A()>:Test.kt") { %composer: Composer?, %changed: Int ->
+                      if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                        A(%composer, 0)
+                      } else {
+                        %composer.skipToGroupEnd()
+                      }
+                    }, %composer, 0b0110)
+                  } else {
+                    %composer.skipToGroupEnd()
+                  }
+                  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    C(%composer, %changed or 0b0001)
+                  }
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                Example(%composer, %changed or 0b0001)
+              }
+            }
+        """,
+        """
+        """
+    )
+
+    @Test
+    fun testStateDelegateCapture(): Unit = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.Composable
+            import androidx.compose.runtime.mutableStateOf
+            import androidx.compose.runtime.getValue
+
+            @Composable fun A() {
+                val x by mutableStateOf(123)
+                B {
+                    print(x)
+                }
+            }
+        """,
+        """
+            @Composable
+            fun A(%composer: Composer?, %changed: Int) {
+              %composer.startRestartGroup(<>, "C(A)<B>:Test.kt")
+              if (%changed !== 0 || !%composer.skipping) {
+                <<LOCALDELPROP>>
+                B(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+                  if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                    print(<get-x>())
+                  } else {
+                    %composer.skipToGroupEnd()
+                  }
+                }, %composer, 0b0110)
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                A(%composer, %changed or 0b0001)
+              }
+            }
+        """,
+        """
+            import androidx.compose.runtime.Composable
+
+            @Composable fun B(content: @Composable () -> Unit) {}
+        """
+    )
+
+    @Test
+    fun testTopLevelComposableLambdaProperties(): Unit = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.Composable
+
+            val foo = @Composable {}
+            val bar: @Composable () -> Unit = {}
+        """,
+        """
+            val foo: Function2<Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+            val bar: Function2<Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-2
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+              val lambda-2: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
+        """,
+        """
+        """
+    )
+
+    @Test
+    fun testLocalVariableComposableLambdas(): Unit = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.Composable
+
+            @Composable fun A() {
+                val foo = @Composable {}
+                val bar: @Composable () -> Unit = {}
+                B(foo)
+                B(bar)
+            }
+        """,
+        """
+            @Composable
+            fun A(%composer: Composer?, %changed: Int) {
+              %composer.startRestartGroup(<>, "C(A)<B(foo)>,<B(bar)>:Test.kt")
+              if (%changed !== 0 || !%composer.skipping) {
+                val foo = ComposableSingletons%TestKt.lambda-1
+                val bar = ComposableSingletons%TestKt.lambda-2
+                B(foo, %composer, 0)
+                B(bar, %composer, 0)
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                A(%composer, %changed or 0b0001)
+              }
+            }
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+              val lambda-2: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
+        """,
+        """
+            import androidx.compose.runtime.Composable
+            @Composable fun B(content: @Composable () -> Unit) {}
+        """
+    )
+
+    @Test
+    fun testParameterComposableLambdas(): Unit = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.Composable
+
+            @Composable fun A() {
+                B {}
+            }
+        """,
+        """
+            @Composable
+            fun A(%composer: Composer?, %changed: Int) {
+              %composer.startRestartGroup(<>, "C(A)<B>:Test.kt")
+              if (%changed !== 0 || !%composer.skipping) {
+                B(ComposableSingletons%TestKt.lambda-1, %composer, 0)
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                A(%composer, %changed or 0b0001)
+              }
+            }
+            internal class ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+                if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
+        """,
+        """
+            import androidx.compose.runtime.Composable
+            @Composable fun B(content: @Composable () -> Unit) {}
+        """
+    )
+
     @Test // regression of b/162575428
     fun testComposableInAFunctionParameter(): Unit = verifyComposeIrTransform(
         """
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
index 3b124e8..bc1a8ea 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
@@ -180,7 +180,7 @@
         import androidx.compose.runtime.*
 
         @Composable inline fun A(
-            lambda: @ComposableContract(preventCapture=true) () -> Unit
+            lambda: @DisallowComposableCalls () -> Unit
         ) { if (Math.random() > 0.5) lambda() }
         @Composable fun B() {}
 
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt
index 6c63e91..93fbd5e 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt
@@ -98,7 +98,7 @@
         doTest(
             """
             import androidx.compose.runtime.*
-            var globalContent = emptyContent()
+            var globalContent = @Composable {}
             fun setContent(content: @Composable () -> Unit) {
                 globalContent = content
             }
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 3638012..5946de2 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
@@ -43,6 +43,9 @@
     val internal = fqNameFor("internal")
     val CurrentComposerIntrinsic = fqNameFor("<get-currentComposer>")
     val ComposableContract = fqNameFor("ComposableContract")
+    val DisallowComposableCalls = fqNameFor("DisallowComposableCalls")
+    val ReadOnlyComposable = fqNameFor("ReadOnlyComposable")
+    val NonRestartableComposable = fqNameFor("NonRestartableComposable")
     val composableLambda = internalFqNameFor("composableLambda")
     val remember = fqNameFor("remember")
     val key = fqNameFor("key")
@@ -80,10 +83,14 @@
 fun Annotated.hasComposableAnnotation(): Boolean =
     annotations.findAnnotation(ComposeFqNames.Composable) != null
 fun Annotated.composableRestartableContract(): Boolean? {
+    val nonrestartable = annotations.findAnnotation(ComposeFqNames.NonRestartableComposable)
+    if (nonrestartable != null) return false
     val contract = annotations.findAnnotation(ComposeFqNames.ComposableContract) ?: return null
     return contract.argumentValue("restartable")?.value as? Boolean
 }
 fun Annotated.composableReadonlyContract(): Boolean? {
+    val readonly = annotations.findAnnotation(ComposeFqNames.ReadOnlyComposable)
+    if (readonly != null) return true
     val contract = annotations.findAnnotation(ComposeFqNames.ComposableContract) ?: return null
     return contract.argumentValue("readonly")?.value as? Boolean
 }
@@ -93,6 +100,8 @@
 }
 
 fun Annotated.composablePreventCaptureContract(): Boolean? {
+    val disallow = annotations.findAnnotation(ComposeFqNames.DisallowComposableCalls)
+    if (disallow != null) return true
     val contract = annotations.findAnnotation(ComposeFqNames.ComposableContract) ?: return null
     return contract.argumentValue("preventCapture")?.value as? Boolean
 }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt
index 4d9386f..9fe3c46 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt
@@ -19,4 +19,8 @@
         BasicWritableSlice(RewritePolicy.DO_NOTHING)
     val IS_SYNTHETIC_COMPOSABLE_CALL: WritableSlice<IrFunctionAccessExpression, Boolean> =
         BasicWritableSlice(RewritePolicy.DO_NOTHING)
+    val IS_COMPOSABLE_SINGLETON: WritableSlice<IrAttributeContainer, Boolean> =
+        BasicWritableSlice(RewritePolicy.DO_NOTHING)
+    val IS_COMPOSABLE_SINGLETON_CLASS: WritableSlice<IrAttributeContainer, Boolean> =
+        BasicWritableSlice(RewritePolicy.DO_NOTHING)
 }
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 d852691..16f0e5a 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
@@ -331,6 +331,14 @@
         return context.irTrace[ComposeWritableSlices.IS_SYNTHETIC_COMPOSABLE_CALL, this] == true
     }
 
+    fun IrCall.isComposableSingletonGetter(): Boolean {
+        return context.irTrace[ComposeWritableSlices.IS_COMPOSABLE_SINGLETON, this] == true
+    }
+
+    fun IrClass.isComposableSingletonClass(): Boolean {
+        return context.irTrace[ComposeWritableSlices.IS_COMPOSABLE_SINGLETON_CLASS, this] == true
+    }
+
     @OptIn(ObsoleteDescriptorBasedAPI::class)
     fun IrFunction.isInlinedLambda(): Boolean {
         descriptor.findPsi()?.let { psi ->
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 ca17dd2..e231aff 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
@@ -653,10 +653,14 @@
 
     private val collectSourceInformation = sourceInformationEnabled
 
-    override fun visitClass(declaration: IrClass): IrStatement =
-        inScope(Scope.ClassScope(declaration.name)) {
+    override fun visitClass(declaration: IrClass): IrStatement {
+        if (declaration.isComposableSingletonClass()) {
+            return declaration
+        }
+        return inScope(Scope.ClassScope(declaration.name)) {
             super.visitDeclaration(declaration)
         }
+    }
 
     override fun visitFunction(declaration: IrFunction): IrStatement {
         val scope = Scope.FunctionScope(declaration, this)
@@ -713,7 +717,7 @@
     // 1. They are inline
     // 2. They have a return value (may get relaxed in the future)
     // 3. They are a lambda (we use ComposableLambda<...> class for this instead)
-    // 4. They are annotated as @ComposableContract(restartable = false)
+    // 4. They are annotated as @NonRestartableComposable
     @OptIn(ObsoleteDescriptorBasedAPI::class)
     private fun IrFunction.shouldBeRestartable(): Boolean {
         // Only insert observe scopes in non-empty composable function
@@ -2522,6 +2526,23 @@
                 recordSourceParameter(expression, 3, composableLambdaScope)
                 return expression
             }
+            expression.isComposableSingletonGetter() -> {
+                // This looks like `ComposableSingletonClass.lambda-123`, which is a static/saved
+                // call of composableLambdaInstance. We want to pass
+                // locations on the top level of the lambda as the startRestartGroup is in the
+                // composable lambda wrapper.
+                val getter = expression.symbol.owner
+                val property = getter.correspondingPropertySymbol?.owner
+                val fieldInitializer = property?.backingField?.initializer?.expression
+                val composableLambdaInstanceCall = fieldInitializer as IrCall
+                val composableLambdaScope = withScope(Scope.ComposableLambdaScope()) {
+                    property.transformChildrenVoid()
+                }
+                if (collectSourceInformation) {
+                    recordSourceParameter(composableLambdaInstanceCall, 2, composableLambdaScope)
+                }
+                return super.visitCall(expression)
+            }
             else -> return super.visitCall(expression)
         }
     }
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 615237a..5d6c1b2 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
@@ -23,28 +23,47 @@
 import androidx.compose.compiler.plugins.kotlin.irTrace
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
+import org.jetbrains.kotlin.backend.common.ir.addChild
+import org.jetbrains.kotlin.backend.common.ir.copyTo
+import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
 import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
 import org.jetbrains.kotlin.backend.common.peek
 import org.jetbrains.kotlin.backend.common.pop
 import org.jetbrains.kotlin.backend.common.push
+import org.jetbrains.kotlin.descriptors.ClassKind
+import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
 import org.jetbrains.kotlin.ir.IrStatement
 import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
 import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
+import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
+import org.jetbrains.kotlin.ir.builders.declarations.addGetter
+import org.jetbrains.kotlin.ir.builders.declarations.addProperty
+import org.jetbrains.kotlin.ir.builders.declarations.buildClass
+import org.jetbrains.kotlin.ir.builders.declarations.buildField
 import org.jetbrains.kotlin.ir.builders.irBlock
+import org.jetbrains.kotlin.ir.builders.irBlockBody
 import org.jetbrains.kotlin.ir.builders.irBoolean
 import org.jetbrains.kotlin.ir.builders.irCall
+import org.jetbrains.kotlin.ir.builders.irDelegatingConstructorCall
+import org.jetbrains.kotlin.ir.builders.irExprBody
 import org.jetbrains.kotlin.ir.builders.irGet
+import org.jetbrains.kotlin.ir.builders.irGetField
 import org.jetbrains.kotlin.ir.builders.irInt
 import org.jetbrains.kotlin.ir.builders.irNull
 import org.jetbrains.kotlin.ir.builders.irReturn
 import org.jetbrains.kotlin.ir.builders.irTemporary
+import org.jetbrains.kotlin.ir.declarations.IrAttributeContainer
+import org.jetbrains.kotlin.ir.declarations.IrClass
 import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
+import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
+import org.jetbrains.kotlin.ir.declarations.IrFile
 import org.jetbrains.kotlin.ir.declarations.IrFunction
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
 import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
 import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
 import org.jetbrains.kotlin.ir.declarations.IrVariable
 import org.jetbrains.kotlin.ir.declarations.copyAttributes
+import org.jetbrains.kotlin.ir.expressions.IrCall
 import org.jetbrains.kotlin.ir.expressions.IrExpression
 import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
 import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
@@ -53,14 +72,20 @@
 import org.jetbrains.kotlin.ir.expressions.IrValueAccessExpression
 import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
 import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
+import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
 import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
 import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
 import org.jetbrains.kotlin.ir.symbols.IrSymbol
+import org.jetbrains.kotlin.ir.types.IrType
 import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
 import org.jetbrains.kotlin.ir.util.defaultType
+import org.jetbrains.kotlin.ir.util.isLocal
 import org.jetbrains.kotlin.ir.util.patchDeclarationParents
+import org.jetbrains.kotlin.ir.util.primaryConstructor
 import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
 import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
+import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
+import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.psi.KtFunctionLiteral
 import org.jetbrains.kotlin.resolve.BindingTrace
 import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -69,10 +94,16 @@
 
 private class CaptureCollector {
     val captures = mutableSetOf<IrValueDeclaration>()
+    val capturedFunctions = mutableSetOf<IrFunction>()
+    val hasCaptures: Boolean get() = captures.isNotEmpty() || capturedFunctions.isNotEmpty()
 
     fun recordCapture(local: IrValueDeclaration) {
         captures.add(local)
     }
+
+    fun recordCapture(local: IrFunction) {
+        capturedFunctions.add(local)
+    }
 }
 
 private abstract class DeclarationContext {
@@ -80,7 +111,9 @@
     abstract val symbol: IrSymbol
     abstract val functionContext: FunctionContext?
     abstract fun declareLocal(local: IrValueDeclaration?)
+    abstract fun recordLocalFunction(local: FunctionContext)
     abstract fun recordCapture(local: IrValueDeclaration?)
+    abstract fun recordCapture(local: IrFunction?)
     abstract fun pushCollector(collector: CaptureCollector)
     abstract fun popCollector(collector: CaptureCollector)
 }
@@ -90,7 +123,9 @@
     override val functionContext: FunctionContext? get() = null
     override val symbol get() = declaration.symbol
     override fun declareLocal(local: IrValueDeclaration?) { }
+    override fun recordLocalFunction(local: FunctionContext) { }
     override fun recordCapture(local: IrValueDeclaration?) { }
+    override fun recordCapture(local: IrFunction?) { }
     override fun pushCollector(collector: CaptureCollector) { }
     override fun popCollector(collector: CaptureCollector) { }
 }
@@ -102,7 +137,10 @@
     override val composable: Boolean get() = functionContext.composable
     override val symbol: IrSymbol get() = declaration.symbol
     override fun declareLocal(local: IrValueDeclaration?) = functionContext.declareLocal(local)
+    override fun recordLocalFunction(local: FunctionContext) =
+        functionContext.recordLocalFunction(local)
     override fun recordCapture(local: IrValueDeclaration?) = functionContext.recordCapture(local)
+    override fun recordCapture(local: IrFunction?) = functionContext.recordCapture(local)
     override fun pushCollector(collector: CaptureCollector) =
         functionContext.pushCollector(collector)
     override fun popCollector(collector: CaptureCollector) =
@@ -117,7 +155,9 @@
     override val symbol get() = declaration.symbol
     override val functionContext: FunctionContext? get() = this
     val locals = mutableSetOf<IrValueDeclaration>()
+    val captures = mutableSetOf<IrValueDeclaration>()
     var collectors = mutableListOf<CaptureCollector>()
+    val localFunctionCaptures = mutableMapOf<IrFunction, Set<IrValueDeclaration>>()
 
     init {
         declaration.valueParameters.forEach {
@@ -133,12 +173,35 @@
         }
     }
 
+    override fun recordLocalFunction(local: FunctionContext) {
+        if (local.captures.isNotEmpty() && local.declaration.isLocal) {
+            localFunctionCaptures[local.declaration] = local.captures
+        }
+    }
+
     override fun recordCapture(local: IrValueDeclaration?) {
         if (local != null && collectors.isNotEmpty() && locals.contains(local)) {
             for (collector in collectors) {
                 collector.recordCapture(local)
             }
         }
+        if (local != null && declaration.isLocal && !locals.contains(local)) {
+            captures.add(local)
+        }
+    }
+
+    override fun recordCapture(local: IrFunction?) {
+        if (local != null) {
+            val captures = localFunctionCaptures[local]
+            for (collector in collectors) {
+                collector.recordCapture(local)
+                if (captures != null) {
+                    for (capture in captures) {
+                        collector.recordCapture(capture)
+                    }
+                }
+            }
+        }
     }
 
     override fun pushCollector(collector: CaptureCollector) {
@@ -151,6 +214,31 @@
     }
 }
 
+private class ClassContext(val declaration: IrClass) : DeclarationContext() {
+    override val composable: Boolean = false
+    override val symbol get() = declaration.symbol
+    override val functionContext: FunctionContext? = null
+    val thisParam: IrValueDeclaration? = declaration.thisReceiver!!
+    var collectors = mutableListOf<CaptureCollector>()
+    override fun declareLocal(local: IrValueDeclaration?) { }
+    override fun recordLocalFunction(local: FunctionContext) { }
+    override fun recordCapture(local: IrValueDeclaration?) {
+        if (local != null && collectors.isNotEmpty() && local == thisParam) {
+            for (collector in collectors) {
+                collector.recordCapture(local)
+            }
+        }
+    }
+    override fun recordCapture(local: IrFunction?) { }
+    override fun pushCollector(collector: CaptureCollector) {
+        collectors.add(collector)
+    }
+    override fun popCollector(collector: CaptureCollector) {
+        require(collectors.lastOrNull() == collector)
+        collectors.removeAt(collectors.size - 1)
+    }
+}
+
 const val COMPOSABLE_LAMBDA = "composableLambda"
 const val COMPOSABLE_LAMBDA_N = "composableLambdaN"
 const val COMPOSABLE_LAMBDA_INSTANCE = "composableLambdaInstance"
@@ -170,6 +258,65 @@
     private val currentFunctionContext: FunctionContext? get() =
         declarationContextStack.peek()?.functionContext
 
+    private var composableSingletonsClass: IrClass? = null
+    private var currentFile: IrFile? = null
+
+    private fun getOrCreateComposableSingletonsClass(): IrClass {
+        if (composableSingletonsClass != null) return composableSingletonsClass!!
+        val declaration = currentFile!!
+        val filePath = declaration.fileEntry.name
+        val fileName = filePath.split('/').last()
+        val current = context.irFactory.buildClass {
+            kind = ClassKind.OBJECT
+            visibility = DescriptorVisibilities.INTERNAL
+            val shortName = PackagePartClassUtils.getFilePartShortName(fileName)
+            // the name of the LiveLiterals class is per-file, so we use the same name that
+            // the kotlin file class lowering produces, prefixed with `LiveLiterals$`.
+            name = Name.identifier("ComposableSingletons${"$"}$shortName")
+        }.also {
+            it.createParameterDeclarations()
+
+            // store the full file path to the file that this class is associated with in an
+            // annotation on the class. This will be used by tooling to associate the keys
+            // inside of this class with actual PSI in the editor.
+            it.addConstructor {
+                isPrimary = true
+            }.also { ctor ->
+                ctor.body = DeclarationIrBuilder(context, it.symbol).irBlockBody {
+                    +irDelegatingConstructorCall(
+                        context
+                            .irBuiltIns
+                            .anyClass
+                            .owner
+                            .primaryConstructor!!
+                    )
+                }
+            }
+        }.markAsComposableSingletonClass()
+        composableSingletonsClass = current
+        return current
+    }
+
+    override fun visitFile(declaration: IrFile): IrFile {
+        val prevFile = currentFile
+        val prevClass = composableSingletonsClass
+        try {
+            currentFile = declaration
+            composableSingletonsClass = null
+            val file = super.visitFile(declaration)
+            // if there were no constants found in the entire file, then we don't need to
+            // create this class at all
+            val resultingClass = composableSingletonsClass
+            if (resultingClass != null && resultingClass.declarations.isNotEmpty()) {
+                file.addChild(resultingClass)
+            }
+            return file
+        } finally {
+            currentFile = prevFile
+            composableSingletonsClass = prevClass
+        }
+    }
+
     override fun lower(module: IrModuleFragment) = module.transformChildrenVoid(this)
 
     override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement {
@@ -214,9 +361,21 @@
             // TODO(b/150390108): Consider allowing remember in effects
             descriptor.returnType.let { it != null && it.isUnit() }
 
-        declarationContextStack.push(FunctionContext(declaration, composable, canRemember))
+        val context = FunctionContext(declaration, composable, canRemember)
+        declarationContextStack.push(context)
         val result = super.visitFunction(declaration)
         declarationContextStack.pop()
+        if (declaration.isLocal) {
+            declarationContextStack.peek()?.recordLocalFunction(context)
+        }
+        return result
+    }
+
+    override fun visitClass(declaration: IrClass): IrStatement {
+        val context = ClassContext(declaration)
+        declarationContextStack.push(context)
+        val result = super.visitClass(declaration)
+        declarationContextStack.pop()
         return result
     }
 
@@ -226,7 +385,9 @@
     }
 
     override fun visitValueAccess(expression: IrValueAccessExpression): IrExpression {
-        declarationContextStack.forEach { it.recordCapture(expression.symbol.owner) }
+        declarationContextStack.forEach {
+            it.recordCapture(expression.symbol.owner)
+        }
         return super.visitValueAccess(expression)
     }
 
@@ -334,12 +495,25 @@
         )
     }
 
+    override fun visitCall(expression: IrCall): IrExpression {
+        val fn = expression.symbol.owner
+        if (fn.isLocal) {
+            declarationContextStack.forEach {
+                it.recordCapture(fn)
+            }
+        }
+        return super.visitCall(expression)
+    }
+
     @ObsoleteDescriptorBasedAPI
     private fun visitComposableFunctionExpression(
         expression: IrFunctionExpression,
         declarationContext: DeclarationContext
     ): IrExpression {
+        val collector = CaptureCollector()
+        startCollector(collector)
         val result = super.visitFunctionExpression(expression)
+        stopCollector(collector)
 
         // If the ancestor converted this then return
         val functionExpression = result as? IrFunctionExpression ?: return result
@@ -352,7 +526,61 @@
             return functionExpression
         }
 
-        return wrapFunctionExpression(declarationContext, functionExpression)
+        val wrapped = wrapFunctionExpression(declarationContext, functionExpression, collector)
+
+        if (!collector.hasCaptures) {
+            return irGetComposableSingleton(
+                lambdaExpression = wrapped,
+                lambdaType = expression.type
+            )
+        } else {
+            return wrapped
+        }
+    }
+
+    private fun irGetComposableSingleton(
+        lambdaExpression: IrExpression,
+        lambdaType: IrType
+    ): IrExpression {
+        val clazz = getOrCreateComposableSingletonsClass()
+        val lambdaName = "lambda-${clazz.declarations.size}"
+        val lambdaProp = clazz.addProperty {
+            name = Name.identifier(lambdaName)
+            visibility = DescriptorVisibilities.INTERNAL
+        }.also { p ->
+            p.backingField = context.irFactory.buildField {
+                name = Name.identifier(lambdaName)
+                type = lambdaType
+                visibility = DescriptorVisibilities.INTERNAL
+                isStatic = true
+            }.also { f ->
+                f.correspondingPropertySymbol = p.symbol
+                f.parent = clazz
+                f.initializer = DeclarationIrBuilder(context, clazz.symbol)
+                    .irExprBody(lambdaExpression)
+            }
+            p.addGetter {
+                returnType = lambdaType
+                visibility = DescriptorVisibilities.INTERNAL
+                origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
+            }.also { fn ->
+                val thisParam = clazz.thisReceiver!!.copyTo(fn)
+                fn.parent = clazz
+                fn.dispatchReceiverParameter = thisParam
+                fn.body = DeclarationIrBuilder(context, fn.symbol).irBlockBody {
+                    +irReturn(irGetField(irGet(thisParam), p.backingField!!))
+                }
+            }
+        }
+        return irCall(
+            lambdaProp.getter!!.symbol,
+            dispatchReceiver = IrGetObjectValueImpl(
+                startOffset = UNDEFINED_OFFSET,
+                endOffset = UNDEFINED_OFFSET,
+                type = clazz.defaultType,
+                symbol = clazz.symbol
+            )
+        ).markAsComposableSingleton()
     }
 
     @ObsoleteDescriptorBasedAPI
@@ -380,13 +608,15 @@
     @ObsoleteDescriptorBasedAPI
     private fun wrapFunctionExpression(
         declarationContext: DeclarationContext,
-        expression: IrFunctionExpression
+        expression: IrFunctionExpression,
+        collector: CaptureCollector
     ): IrExpression {
         val function = expression.function
         val argumentCount = function.descriptor.valueParameters.size
         val useComposableLambdaN = argumentCount > MAX_RESTART_ARGUMENT_COUNT
+        val useComposableFactory = collector.hasCaptures && declarationContext.composable
         val restartFunctionFactory =
-            if (declarationContext.composable)
+            if (useComposableFactory)
                 if (useComposableLambdaN)
                     COMPOSABLE_LAMBDA_N
                 else COMPOSABLE_LAMBDA
@@ -406,8 +636,8 @@
         return irBuilder.irCall(restartFactorySymbol).apply {
             var index = 0
 
-            // first parameter is the composer parameter if we are in a composable context
-            if (declarationContext.composable) {
+            // first parameter is the composer parameter if we are using the composable factory
+            if (useComposableFactory) {
                 putValueArgument(
                     index++,
                     irCurrentComposer()
@@ -424,12 +654,13 @@
             )
 
             // tracked parameter
-            putValueArgument(index++, irBuilder.irBoolean(expression.isTracked()))
+            // If the lambda has no captures, then kotlin will turn it into a singleton instance,
+            // which means that it will never change, thus does not need to be tracked.
+            val shouldBeTracked = expression.isTracked() && collector.captures.isNotEmpty()
+            putValueArgument(index++, irBuilder.irBoolean(shouldBeTracked))
 
-            if (declarationContext.composable) {
-                // sourceInformation parameter
-                putValueArgument(index++, irBuilder.irNull())
-            }
+            // sourceInformation parameter
+            putValueArgument(index++, irBuilder.irNull())
 
             // ComposableLambdaN requires the arity
             if (useComposableLambdaN) {
@@ -551,6 +782,28 @@
         return this
     }
 
+    private fun <T : IrAttributeContainer> T.markAsComposableSingleton(): T {
+        // Mark it so the ComposableCallTransformer can insert the correct source information
+        // around this call
+        context.irTrace.record(
+            ComposeWritableSlices.IS_COMPOSABLE_SINGLETON,
+            this,
+            true
+        )
+        return this
+    }
+
+    private fun <T : IrAttributeContainer> T.markAsComposableSingletonClass(): T {
+        // Mark it so the ComposableCallTransformer can insert the correct source information
+        // around this call
+        context.irTrace.record(
+            ComposeWritableSlices.IS_COMPOSABLE_SINGLETON_CLASS,
+            this,
+            true
+        )
+        return this
+    }
+
     @OptIn(ObsoleteDescriptorBasedAPI::class)
     private fun IrFunctionExpression.isInlineArgument(): Boolean {
         function.descriptor.findPsi()?.let { psi ->
diff --git a/compose/desktop/desktop/samples/build.gradle b/compose/desktop/desktop/samples/build.gradle
index 3e0e213..01e355e 100644
--- a/compose/desktop/desktop/samples/build.gradle
+++ b/compose/desktop/desktop/samples/build.gradle
@@ -48,6 +48,7 @@
 task run1(type: JavaExec) {
     dependsOn(":compose:desktop:desktop:jar")
     main = 'androidx.compose.desktop.examples.example1.MainKt'
+    systemProperty("skiko.fps.enabled", "true")
     def compilation = kotlin.jvm().compilations["main"]
     classpath =
         compilation.output.allOutputs +
@@ -81,6 +82,16 @@
         compilation.runtimeDependencyFiles
 }
 
+task runVsync(type: JavaExec) {
+    dependsOn(":compose:desktop:desktop:jar")
+    main = 'androidx.compose.desktop.examples.vsynctest.MainKt'
+    jvmArgs("-verbose:gc")
+    def compilation = kotlin.jvm().compilations["main"]
+    classpath =
+        compilation.output.allOutputs +
+        compilation.runtimeDependencyFiles
+}
+
 task run {
     dependsOn("run1")
 }
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt
index 6e75ea9..26e0224 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt
@@ -48,13 +48,14 @@
 import java.awt.event.ActionListener
 import javax.swing.JButton
 import javax.swing.JFrame
+import javax.swing.SwingUtilities
 import javax.swing.WindowConstants
 
 val northClicks = mutableStateOf(0)
 val westClicks = mutableStateOf(0)
 val eastClicks = mutableStateOf(0)
 
-fun main() {
+fun main() = SwingUtilities.invokeLater {
     // explicitly clear the application events
     AppManager.setEvents(
         onAppStart = null,
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/vsynctest/Main.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/vsynctest/Main.kt
new file mode 100644
index 0000000..59b3714
--- /dev/null
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/vsynctest/Main.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.compose.desktop.examples.vsynctest
+
+import androidx.compose.desktop.AppWindowAmbient
+import androidx.compose.desktop.Window
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.withFrameMillis
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.AmbientDensity
+import androidx.compose.ui.unit.IntSize
+
+private val frameLogCount = 1000
+private val red = Color(255, 128, 128)
+private val cyan = Color(128, 255, 255)
+
+fun main() {
+    window()
+    window()
+}
+
+fun window() {
+    var t1 = Long.MAX_VALUE
+    val frameDeltas = ArrayList<Long>(10000)
+    var heuristicExpectedFrameTime = -1L
+
+    fun logFrame() {
+        val t2 = System.nanoTime()
+        val dt = (t2 - t1).coerceAtLeast(0)
+        frameDeltas.add(dt)
+        t1 = t2
+
+        if (heuristicExpectedFrameTime > 0 && dt > heuristicExpectedFrameTime * 1.5) {
+            val dtMillis = dt / 1E6
+            val expectedMillis = heuristicExpectedFrameTime / 1E6
+            println("Too long frame %.2f (expected %.2f)".format(dtMillis, expectedMillis))
+        }
+
+        if (frameDeltas.size % frameLogCount == 0) {
+            val fps = 1E9 / frameDeltas.average()
+
+            // it is more precise than
+            // window.window.graphicsConfiguration.device.displayMode.refreshRate
+            // if vsync is supported
+            heuristicExpectedFrameTime = frameDeltas.median()
+
+            val actualFrameCount = frameDeltas.sum() / heuristicExpectedFrameTime
+            val missedFrames = (actualFrameCount - frameDeltas.size).coerceAtLeast(0)
+            val missedFrameCountPercent = 100.0 * missedFrames / frameDeltas.size
+            println("FPS %.2f, missed frames %.2f%%".format(fps, missedFrameCountPercent))
+            frameDeltas.clear()
+        }
+    }
+
+    Window(size = IntSize(800, 200)) {
+        val window = AppWindowAmbient.current!!
+        val width = (AmbientDensity.current.density * window.window.width).toInt()
+        val singleFrameMillis = remember {
+            1000 / window.window.graphicsConfiguration.device.displayMode.refreshRate
+        }
+        var position1 by remember { mutableStateOf(0L) }
+        var position2 by remember { mutableStateOf(0L) }
+        var isOddFrame by remember { mutableStateOf(false) }
+
+        LaunchedEffect(Unit) {
+            while (true) {
+                withFrameMillis {
+                    position1 = it % width
+                    position2 = (it / 4) % width
+                }
+            }
+        }
+
+        Canvas(Modifier.fillMaxSize()) {
+            for (x in 0..width step singleFrameMillis) {
+                drawLine(Color.Black, Offset(x.toFloat(), 0f), Offset(x.toFloat(), 10f))
+            }
+
+            drawRect(Color.Red, Offset(position1.toFloat(), 10f), Size(32f, 32f))
+            drawRect(Color.Red, Offset(position2.toFloat(), 50f), Size(32f, 32f))
+
+            // test similar to https://www.vsynctester.com/
+            drawRect(if (isOddFrame) red else cyan, Offset(10f, 120f), Size(50f, 50f))
+            isOddFrame = !isOddFrame
+
+            logFrame()
+        }
+    }
+}
+
+private fun List<Long>.median() = sorted()[size / 2]
\ No newline at end of file
diff --git a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/WindowDraggableArea.kt b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/WindowDraggableArea.kt
index 60b07ce..a058795 100644
--- a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/WindowDraggableArea.kt
+++ b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/WindowDraggableArea.kt
@@ -19,7 +19,6 @@
 import androidx.compose.desktop.AppManager
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
@@ -30,7 +29,7 @@
 @Composable
 fun WindowDraggableArea(
     modifier: Modifier = Modifier,
-    content: @Composable() () -> Unit = emptyContent()
+    content: @Composable() () -> Unit = {}
 ) {
     Box(
         modifier = modifier.dragGestureFilter(
diff --git a/compose/desktop/desktop/src/jvmTest/kotlin/androidx/compose/desktop/ParagraphTest.kt b/compose/desktop/desktop/src/jvmTest/kotlin/androidx/compose/desktop/ParagraphTest.kt
index 0ea23e4..c46473e 100644
--- a/compose/desktop/desktop/src/jvmTest/kotlin/androidx/compose/desktop/ParagraphTest.kt
+++ b/compose/desktop/desktop/src/jvmTest/kotlin/androidx/compose/desktop/ParagraphTest.kt
@@ -16,8 +16,6 @@
 
 package androidx.ui.desktop
 
-import androidx.compose.material.ProvideTextStyle
-import androidx.compose.material.Text
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
@@ -25,19 +23,21 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material.ProvideTextStyle
+import androidx.compose.material.Text
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.TestComposeWindow
+import androidx.compose.ui.test.junit4.DesktopScreenshotTestRule
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontStyle
 import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.platform.Font
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
-import androidx.compose.ui.test.junit4.DesktopScreenshotTestRule
-import androidx.compose.ui.test.TestComposeWindow
-import androidx.compose.ui.text.platform.Font
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
diff --git a/compose/foundation/foundation-layout/api/restricted_current.txt b/compose/foundation/foundation-layout/api/restricted_current.txt
index 59bc66e..ed398b4 100644
--- a/compose/foundation/foundation-layout/api/restricted_current.txt
+++ b/compose/foundation/foundation-layout/api/restricted_current.txt
@@ -387,7 +387,6 @@
   }
 
   public final class RowColumnImplKt {
-    method @kotlin.PublishedApi internal static androidx.compose.ui.node.MeasureBlocks rowColumnMeasureBlocks-GZ6WFlY(androidx.compose.foundation.layout.LayoutOrientation orientation, kotlin.jvm.functions.Function5<? super java.lang.Integer,? super int[],? super androidx.compose.ui.unit.LayoutDirection,? super androidx.compose.ui.unit.Density,? super int[],kotlin.Unit> arrangement, float arrangementSpacing, androidx.compose.foundation.layout.SizeMode crossAxisSize, androidx.compose.foundation.layout.CrossAxisAlignment crossAxisAlignment);
   }
 
   public final class RowKt {
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt
index 4b170d8..0584869 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt
@@ -17,7 +17,6 @@
 package androidx.compose.foundation.layout
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
@@ -483,7 +482,7 @@
     maxIntrinsicHeight: Dp
 ) {
     Layout(
-        emptyContent(),
+        {},
         minIntrinsicWidthMeasureBlock = { _, _ -> minIntrinsicWidth.toIntPx() },
         minIntrinsicHeightMeasureBlock = { _, _ -> minIntrinsicHeight.toIntPx() },
         maxIntrinsicWidthMeasureBlock = { _, _ -> maxIntrinsicWidth.toIntPx() },
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
index 392bf97..3326100 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
@@ -19,7 +19,6 @@
 import androidx.compose.ui.layout.FirstBaseline
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
-import androidx.compose.runtime.emptyContent
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.layout.HorizontalAlignmentLine
 import androidx.compose.ui.layout.Layout
@@ -1342,12 +1341,12 @@
                         Modifier.weight(1f),
                         width = sizeDp,
                         height = sizeDp,
-                        content = emptyContent()
+                        content = {}
                     )
                     Container(
                         width = (sizeDp * 2),
                         height = (sizeDp * 2),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
@@ -1476,12 +1475,12 @@
                             Modifier.weight(1f),
                             width = sizeDp,
                             height = sizeDp,
-                            content = emptyContent()
+                            content = {}
                         )
                         Container(
                             width = sizeDp * 2,
                             height = sizeDp * 2,
-                            content = emptyContent()
+                            content = {}
                         )
                     }
                 }
@@ -1812,12 +1811,12 @@
                         Modifier.weight(1f),
                         width = sizeDp,
                         height = sizeDp,
-                        content = emptyContent()
+                        content = {}
                     )
                     Container(
                         width = (sizeDp * 2),
                         height = (sizeDp * 2),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
@@ -1946,12 +1945,12 @@
                             Modifier.weight(1f),
                             width = sizeDp,
                             height = sizeDp,
-                            content = emptyContent()
+                            content = {}
                         )
                         Container(
                             width = sizeDp * 2,
                             height = sizeDp * 2,
-                            content = emptyContent()
+                            content = {}
                         )
                     }
                 }
@@ -2139,7 +2138,7 @@
                                 assertEquals(Constraints(), constraints)
                                 FixedSizeLayout(0, noWeightChildHeight.toIntPx(), mapOf())
                             }
-                            Layout(emptyContent(), Modifier.weight(1f)) { _, constraints ->
+                            Layout({}, Modifier.weight(1f)) { _, constraints ->
                                 assertEquals(
                                     columnMinHeight.toIntPx() - noWeightChildHeight.toIntPx() * 2,
                                     constraints.minHeight
@@ -3206,19 +3205,19 @@
         testIntrinsics(
             @Composable {
                 Row {
-                    Container(Modifier.aspectRatio(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
             @Composable {
                 Row(Modifier.fillMaxWidth()) {
-                    Container(Modifier.aspectRatio(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3227,12 +3226,12 @@
                     Container(
                         Modifier.aspectRatio(2f)
                             .align(Alignment.Top),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
                         Modifier.align(Alignment.CenterVertically),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3240,21 +3239,21 @@
                 Row {
                     Container(
                         Modifier.aspectRatio(2f).alignBy(FirstBaseline),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
                         Modifier.alignBy { it.width },
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
             @Composable {
                 Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Start) {
-                    Container(Modifier.aspectRatio(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3262,12 +3261,12 @@
                 Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
                     Container(
                         Modifier.align(Alignment.CenterVertically).aspectRatio(2f),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
                         Modifier.align(Alignment.CenterVertically),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3275,40 +3274,40 @@
                 Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
                     Container(
                         Modifier.align(Alignment.Bottom).aspectRatio(2f),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
                         Modifier.align(Alignment.Bottom),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
             @Composable {
                 Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround) {
-                    Container(Modifier.fillMaxHeight().aspectRatio(2f), content = emptyContent())
+                    Container(Modifier.fillMaxHeight().aspectRatio(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
                         Modifier.fillMaxHeight(),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
             @Composable {
                 Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
-                    Container(Modifier.aspectRatio(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
             @Composable {
                 Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
-                    Container(Modifier.aspectRatio(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
@@ -3346,17 +3345,17 @@
                     ConstrainedBox(
                         DpConstraints.fixed(20.toDp(), 30.toDp()),
                         Modifier.weight(3f),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(30.toDp(), 40.toDp()),
                         Modifier.weight(2f),
-                        content = emptyContent()
+                        content = {}
                     )
-                    Container(Modifier.aspectRatio(2f).weight(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f).weight(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(20.toDp(), 30.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3365,18 +3364,18 @@
                     ConstrainedBox(
                         DpConstraints.fixed(20.toDp(), 30.toDp()),
                         Modifier.weight(3f).align(Alignment.Top),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(30.toDp(), 40.toDp()),
                         Modifier.weight(2f).align(Alignment.CenterVertically),
-                        content = emptyContent()
+                        content = {}
                     )
-                    Container(Modifier.aspectRatio(2f).weight(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f).weight(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(20.toDp(), 30.toDp()),
                         Modifier.align(Alignment.Bottom),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3385,17 +3384,17 @@
                     ConstrainedBox(
                         DpConstraints.fixed(20.toDp(), 30.toDp()),
                         Modifier.weight(3f),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(30.toDp(), 40.toDp()),
                         Modifier.weight(2f),
-                        content = emptyContent()
+                        content = {}
                     )
-                    Container(Modifier.aspectRatio(2f).weight(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f).weight(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(20.toDp(), 30.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3404,21 +3403,21 @@
                     ConstrainedBox(
                         constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
                         modifier = Modifier.weight(3f).align(Alignment.CenterVertically),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         constraints = DpConstraints.fixed(30.toDp(), 40.toDp()),
                         modifier = Modifier.weight(2f).align(Alignment.CenterVertically),
-                        content = emptyContent()
+                        content = {}
                     )
                     Container(
                         Modifier.aspectRatio(2f).weight(2f).align(Alignment.CenterVertically),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
                         modifier = Modifier.align(Alignment.CenterVertically),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3427,21 +3426,21 @@
                     ConstrainedBox(
                         constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
                         modifier = Modifier.weight(3f).align(Alignment.Bottom),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         constraints = DpConstraints.fixed(30.toDp(), 40.toDp()),
                         modifier = Modifier.weight(2f).align(Alignment.Bottom),
-                        content = emptyContent()
+                        content = {}
                     )
                     Container(
                         Modifier.aspectRatio(2f).weight(2f).align(Alignment.Bottom),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
                         modifier = Modifier.align(Alignment.Bottom),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3450,21 +3449,21 @@
                     ConstrainedBox(
                         constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
                         modifier = Modifier.weight(3f).fillMaxHeight(),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         constraints = DpConstraints.fixed(30.toDp(), 40.toDp()),
                         modifier = Modifier.weight(2f).fillMaxHeight(),
-                        content = emptyContent()
+                        content = {}
                     )
                     Container(
                         Modifier.aspectRatio(2f).weight(2f).fillMaxHeight(),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
                         modifier = Modifier.fillMaxHeight(),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3473,17 +3472,17 @@
                     ConstrainedBox(
                         DpConstraints.fixed(20.toDp(), 30.toDp()),
                         Modifier.weight(3f),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(30.toDp(), 40.toDp()),
                         Modifier.weight(2f),
-                        content = emptyContent()
+                        content = {}
                     )
-                    Container(Modifier.aspectRatio(2f).weight(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f).weight(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(20.toDp(), 30.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3492,17 +3491,17 @@
                     ConstrainedBox(
                         DpConstraints.fixed(20.toDp(), 30.toDp()),
                         Modifier.weight(3f),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(30.toDp(), 40.toDp()),
                         Modifier.weight(2f),
-                        content = emptyContent()
+                        content = {}
                     )
-                    Container(Modifier.aspectRatio(2f).weight(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f).weight(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(20.toDp(), 30.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
@@ -3559,10 +3558,10 @@
         testIntrinsics(
             @Composable {
                 Column {
-                    Container(Modifier.aspectRatio(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3570,12 +3569,12 @@
                 Column {
                     Container(
                         Modifier.aspectRatio(2f).align(Alignment.Start),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
                         Modifier.align(Alignment.End),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3583,30 +3582,30 @@
                 Column {
                     Container(
                         Modifier.aspectRatio(2f).alignBy { 0 },
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
                         Modifier.alignBy(TestVerticalLine),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
             @Composable {
                 Column(Modifier.fillMaxHeight()) {
-                    Container(Modifier.aspectRatio(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
             @Composable {
                 Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.Top) {
-                    Container(Modifier.aspectRatio(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3614,11 +3613,11 @@
                 Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.Center) {
                     Container(
                         Modifier.align(Alignment.CenterHorizontally).aspectRatio(2f),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3626,31 +3625,31 @@
                 Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.Bottom) {
                     Container(
                         Modifier.align(Alignment.End).aspectRatio(2f),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
                         Modifier.align(Alignment.End),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
             @Composable {
                 Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.SpaceAround) {
-                    Container(Modifier.fillMaxWidth().aspectRatio(2f), content = emptyContent())
+                    Container(Modifier.fillMaxWidth().aspectRatio(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
                         Modifier.fillMaxWidth(),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
             @Composable {
                 Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.SpaceBetween) {
-                    Container(Modifier.aspectRatio(2f), content = emptyContent())
+                    Container(Modifier.aspectRatio(2f), content = {})
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3658,11 +3657,11 @@
                 Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.SpaceEvenly) {
                     Container(
                         Modifier.aspectRatio(2f),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(50.toDp(), 40.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
@@ -3700,20 +3699,20 @@
                     ConstrainedBox(
                         DpConstraints.fixed(30.toDp(), 20.toDp()),
                         Modifier.weight(3f),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(40.toDp(), 30.toDp()),
                         Modifier.weight(2f),
-                        content = emptyContent()
+                        content = {}
                     )
                     Container(
                         Modifier.aspectRatio(0.5f).weight(2f),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(30.toDp(), 20.toDp()),
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             },
@@ -3722,12 +3721,12 @@
                     ConstrainedBox(
                         DpConstraints.fixed(30.toDp(), 20.toDp()),
                         Modifier.weight(3f).align(Alignment.Start),
-                        content = emptyContent()
+                        content = {}
                     )
                     ConstrainedBox(
                         DpConstraints.fixed(40.toDp(), 30.toDp()),
                         Modifier.weight(2f).align(Alignment.CenterHorizontally),
-                        content = emptyContent()
+                        content = {}
                     )
                     Container(Modifier.aspectRatio(0.5f).weight(2f)) { }
                     ConstrainedBox(
@@ -3964,9 +3963,9 @@
                                 containerHeight.value = coordinates.size.height
                                 positionedLatch.countDown()
                             },
-                        content = emptyContent()
+                        content = {}
                     )
-                    Container(Modifier.weight(1f), content = emptyContent())
+                    Container(Modifier.weight(1f), content = {})
                 }
             }
         }
@@ -3990,7 +3989,7 @@
                     modifier = Modifier.alignBy { it.height },
                     width = size,
                     height = size,
-                    content = emptyContent()
+                    content = {}
                 )
                 Container(
                     modifier = Modifier.alignBy { 0 }
@@ -4002,7 +4001,7 @@
                         },
                     width = size,
                     height = size,
-                    content = emptyContent()
+                    content = {}
                 )
             }
         }
@@ -4085,7 +4084,7 @@
                                 childLayoutCoordinates[i] = coordinates
                                 drawLatch.countDown()
                             },
-                            content = emptyContent()
+                            content = {}
                         )
                     }
                 }
@@ -4141,7 +4140,7 @@
                                 childLayoutCoordinates[i] = coordinates
                                 drawLatch.countDown()
                             },
-                            content = emptyContent()
+                            content = {}
                         )
                     }
                 }
@@ -4196,7 +4195,7 @@
                                 childLayoutCoordinates[i] = coordinates
                                 drawLatch.countDown()
                             },
-                            content = emptyContent()
+                            content = {}
                         )
                     }
                 }
@@ -4249,7 +4248,7 @@
                                 childLayoutCoordinates[i] = coordinates
                                 drawLatch.countDown()
                             },
-                            content = emptyContent()
+                            content = {}
                         )
                     }
                 }
@@ -4521,7 +4520,7 @@
                             childLayoutCoordinates[i] = coordinates
                             drawLatch.countDown()
                         },
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
@@ -4575,7 +4574,7 @@
                                     coordinates
                                 drawLatch.countDown()
                             },
-                            content = emptyContent()
+                            content = {}
                         )
                     }
                 }
@@ -4628,7 +4627,7 @@
                             childLayoutCoordinates[i] = coordinates
                             drawLatch.countDown()
                         },
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
@@ -4688,7 +4687,7 @@
                                     coordinates
                                 drawLatch.countDown()
                             },
-                            content = emptyContent()
+                            content = {}
                         )
                     }
                 }
@@ -4747,7 +4746,7 @@
                             childLayoutCoordinates[i] = coordinates
                             drawLatch.countDown()
                         },
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
@@ -4817,7 +4816,7 @@
                                     coordinates
                                 drawLatch.countDown()
                             },
-                            content = emptyContent()
+                            content = {}
                         )
                     }
                 }
@@ -4886,7 +4885,7 @@
                             childLayoutCoordinates[i] = coordinates
                             drawLatch.countDown()
                         },
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
@@ -4954,7 +4953,7 @@
                                         coordinates
                                     drawLatch.countDown()
                                 },
-                                content = emptyContent()
+                                content = {}
                             )
                         }
                     }
@@ -5020,7 +5019,7 @@
                             childLayoutCoordinates[i] = coordinates
                             drawLatch.countDown()
                         },
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
@@ -5086,7 +5085,7 @@
                                         coordinates
                                     drawLatch.countDown()
                                 },
-                                content = emptyContent()
+                                content = {}
                             )
                         }
                     }
@@ -5149,7 +5148,7 @@
                             childLayoutCoordinates[i] = coordinates
                             drawLatch.countDown()
                         },
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
@@ -5218,7 +5217,7 @@
                                         coordinates
                                     drawLatch.countDown()
                                 },
-                                content = emptyContent()
+                                content = {}
                             )
                         }
                     }
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
index 5f460cb..2eddbde 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
@@ -17,7 +17,6 @@
 package androidx.compose.foundation.layout
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.Providers
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.layout.Layout
@@ -1295,7 +1294,7 @@
         // Clear contents before each test so that we don't recompose the BoxWithConstraints call;
         // doing so would recompose the old subcomposition with old constraints in the presence of
         // new content before the measurement performs explicit composition the new constraints.
-        show(emptyContent())
+        show({})
         show {
             Layout({
                 BoxWithConstraints(modifier) {
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
index 2a099b83..b0c3cbb 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
@@ -41,7 +41,6 @@
 import kotlin.math.roundToInt
 import kotlin.math.sign
 
-@PublishedApi
 internal fun rowColumnMeasureBlocks(
     orientation: LayoutOrientation,
     arrangement: (Int, IntArray, LayoutDirection, Density, IntArray) -> Unit,
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt
index 6db4f41..46170b4 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt
@@ -17,7 +17,6 @@
 package androidx.compose.foundation.layout
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.hasFixedHeight
@@ -33,7 +32,7 @@
  */
 @Composable
 fun Spacer(modifier: Modifier) {
-    Layout(emptyContent(), modifier) { _, constraints ->
+    Layout({}, modifier) { _, constraints ->
         with(constraints) {
             val width = if (hasFixedWidth) maxWidth else 0
             val height = if (hasFixedHeight) maxHeight else 0
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 2640b07..b2e0d0d 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -49,7 +49,7 @@
   }
 
   public final class DarkThemeKt {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public static boolean isSystemInDarkTheme();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static boolean isSystemInDarkTheme();
   }
 
   @kotlin.RequiresOptIn(message="This foundation API is experimental and is likely to change or be removed in the " + "future.") public @interface ExperimentalFoundationApi {
@@ -193,10 +193,10 @@
   public final class FloatAndroidFlingDecaySpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
     ctor public FloatAndroidFlingDecaySpec(androidx.compose.ui.unit.Density density);
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(float start, float startVelocity);
-    method public float getTarget(float start, float startVelocity);
-    method public float getValue(long playTime, float start, float startVelocity);
-    method public float getVelocity(long playTime, float start, float startVelocity);
+    method public long getDurationNanos(float initialValue, float initialVelocity);
+    method public float getTargetValue(float initialValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
     property public float absVelocityThreshold;
   }
 
@@ -302,15 +302,15 @@
   public final class CachingItemContentFactoryKt {
   }
 
-  public abstract sealed class GridCells {
+  @androidx.compose.foundation.ExperimentalFoundationApi public abstract sealed class GridCells {
   }
 
-  public static final class GridCells.Adaptive extends androidx.compose.foundation.lazy.GridCells {
+  @androidx.compose.foundation.ExperimentalFoundationApi public static final class GridCells.Adaptive extends androidx.compose.foundation.lazy.GridCells {
     method public float getMinSize-D9Ej5fM();
     property public final float minSize;
   }
 
-  public static final class GridCells.Fixed extends androidx.compose.foundation.lazy.GridCells {
+  @androidx.compose.foundation.ExperimentalFoundationApi public static final class GridCells.Fixed extends androidx.compose.foundation.lazy.GridCells {
     ctor public GridCells.Fixed(int count);
     method public int getCount();
     property public final int count;
@@ -334,13 +334,13 @@
 
   public final class LazyGridKt {
     method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.compose.foundation.lazy.GridCells cells, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyGridScope,kotlin.Unit> content);
-    method public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
-    method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
   }
 
-  public interface LazyGridScope {
+  @androidx.compose.foundation.ExperimentalFoundationApi public interface LazyGridScope {
     method public void item(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
     method public void items(int count, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
   }
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index 2640b07..b2e0d0d 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -49,7 +49,7 @@
   }
 
   public final class DarkThemeKt {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public static boolean isSystemInDarkTheme();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static boolean isSystemInDarkTheme();
   }
 
   @kotlin.RequiresOptIn(message="This foundation API is experimental and is likely to change or be removed in the " + "future.") public @interface ExperimentalFoundationApi {
@@ -193,10 +193,10 @@
   public final class FloatAndroidFlingDecaySpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
     ctor public FloatAndroidFlingDecaySpec(androidx.compose.ui.unit.Density density);
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(float start, float startVelocity);
-    method public float getTarget(float start, float startVelocity);
-    method public float getValue(long playTime, float start, float startVelocity);
-    method public float getVelocity(long playTime, float start, float startVelocity);
+    method public long getDurationNanos(float initialValue, float initialVelocity);
+    method public float getTargetValue(float initialValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
     property public float absVelocityThreshold;
   }
 
@@ -302,15 +302,15 @@
   public final class CachingItemContentFactoryKt {
   }
 
-  public abstract sealed class GridCells {
+  @androidx.compose.foundation.ExperimentalFoundationApi public abstract sealed class GridCells {
   }
 
-  public static final class GridCells.Adaptive extends androidx.compose.foundation.lazy.GridCells {
+  @androidx.compose.foundation.ExperimentalFoundationApi public static final class GridCells.Adaptive extends androidx.compose.foundation.lazy.GridCells {
     method public float getMinSize-D9Ej5fM();
     property public final float minSize;
   }
 
-  public static final class GridCells.Fixed extends androidx.compose.foundation.lazy.GridCells {
+  @androidx.compose.foundation.ExperimentalFoundationApi public static final class GridCells.Fixed extends androidx.compose.foundation.lazy.GridCells {
     ctor public GridCells.Fixed(int count);
     method public int getCount();
     property public final int count;
@@ -334,13 +334,13 @@
 
   public final class LazyGridKt {
     method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.compose.foundation.lazy.GridCells cells, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyGridScope,kotlin.Unit> content);
-    method public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
-    method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
   }
 
-  public interface LazyGridScope {
+  @androidx.compose.foundation.ExperimentalFoundationApi public interface LazyGridScope {
     method public void item(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
     method public void items(int count, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
   }
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 2640b07..b2e0d0d 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -49,7 +49,7 @@
   }
 
   public final class DarkThemeKt {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public static boolean isSystemInDarkTheme();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static boolean isSystemInDarkTheme();
   }
 
   @kotlin.RequiresOptIn(message="This foundation API is experimental and is likely to change or be removed in the " + "future.") public @interface ExperimentalFoundationApi {
@@ -193,10 +193,10 @@
   public final class FloatAndroidFlingDecaySpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
     ctor public FloatAndroidFlingDecaySpec(androidx.compose.ui.unit.Density density);
     method public float getAbsVelocityThreshold();
-    method public long getDurationMillis(float start, float startVelocity);
-    method public float getTarget(float start, float startVelocity);
-    method public float getValue(long playTime, float start, float startVelocity);
-    method public float getVelocity(long playTime, float start, float startVelocity);
+    method public long getDurationNanos(float initialValue, float initialVelocity);
+    method public float getTargetValue(float initialValue, float initialVelocity);
+    method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+    method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
     property public float absVelocityThreshold;
   }
 
@@ -302,15 +302,15 @@
   public final class CachingItemContentFactoryKt {
   }
 
-  public abstract sealed class GridCells {
+  @androidx.compose.foundation.ExperimentalFoundationApi public abstract sealed class GridCells {
   }
 
-  public static final class GridCells.Adaptive extends androidx.compose.foundation.lazy.GridCells {
+  @androidx.compose.foundation.ExperimentalFoundationApi public static final class GridCells.Adaptive extends androidx.compose.foundation.lazy.GridCells {
     method public float getMinSize-D9Ej5fM();
     property public final float minSize;
   }
 
-  public static final class GridCells.Fixed extends androidx.compose.foundation.lazy.GridCells {
+  @androidx.compose.foundation.ExperimentalFoundationApi public static final class GridCells.Fixed extends androidx.compose.foundation.lazy.GridCells {
     ctor public GridCells.Fixed(int count);
     method public int getCount();
     property public final int count;
@@ -334,13 +334,13 @@
 
   public final class LazyGridKt {
     method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.compose.foundation.lazy.GridCells cells, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyGridScope,kotlin.Unit> content);
-    method public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
-    method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
-    method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void items(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyGridScope, T![] items, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
   }
 
-  public interface LazyGridScope {
+  @androidx.compose.foundation.ExperimentalFoundationApi public interface LazyGridScope {
     method public void item(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
     method public void items(int count, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
   }
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
index f033246..51a1925 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
@@ -37,13 +37,13 @@
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentSize
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.LazyVerticalGrid
 import androidx.compose.foundation.lazy.GridCells
+import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.LazyRow
-import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.lazy.LazyVerticalGrid
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.samples.StickyHeaderSample
 import androidx.compose.foundation.shape.RoundedCornerShape
@@ -58,7 +58,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -472,7 +472,7 @@
             Modifier.padding(16.dp).size(200.dp).background(Color.LightGray),
             contentAlignment = Alignment.Center
         ) {
-            var state by savedInstanceState { 0 }
+            var state by rememberSaveable { mutableStateOf(0) }
             Button(onClick = { state++ }) {
                 Text("Index=$index State=$state")
             }
@@ -492,6 +492,7 @@
     }
 }
 
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
 private fun LazyGridDemo() {
     val columnModes = listOf(
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/CapitalizationAutoCorrectDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/CapitalizationAutoCorrectDemo.kt
index d4cb846..ae9ddb2 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/CapitalizationAutoCorrectDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/CapitalizationAutoCorrectDemo.kt
@@ -24,7 +24,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
@@ -93,7 +93,9 @@
 )
 private fun MyTextField(data: ImeOptionsData) {
     val controller = remember { mutableStateOf<SoftwareKeyboardController?>(null) }
-    val state = savedInstanceState(saver = TextFieldValue.Saver) { TextFieldValue() }
+    val state = rememberSaveable(stateSaver = TextFieldValue.Saver) {
+        mutableStateOf(TextFieldValue())
+    }
     BasicTextField(
         modifier = demoTextFieldModifiers.defaultMinSizeConstraints(100.dp),
         value = state.value,
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputField.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputField.kt
index d83f336..ca65435 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputField.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputField.kt
@@ -28,7 +28,7 @@
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.AmbientLayoutDirection
@@ -81,7 +81,7 @@
     text: String = ""
 ) {
     val controller = remember { mutableStateOf<SoftwareKeyboardController?>(null) }
-    val state = savedInstanceState { text }
+    val state = rememberSaveable { mutableStateOf(text) }
     BasicTextField(
         modifier = demoTextFieldModifiers,
         value = state.value,
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldFocusTransition.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldFocusTransition.kt
index 698b35d..b9b210f 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldFocusTransition.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldFocusTransition.kt
@@ -24,7 +24,7 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
@@ -53,7 +53,7 @@
     focusRequester: FocusRequester,
     nextFocusRequester: FocusRequester
 ) {
-    val state = savedInstanceState { "Focus Transition Test" }
+    val state = rememberSaveable { mutableStateOf("Focus Transition Test") }
     var color by remember { mutableStateOf(Black) }
 
     BasicTextField(
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldMinMaxLines.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldMinMaxLines.kt
index eb755df..3fb1007 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldMinMaxLines.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldMinMaxLines.kt
@@ -19,7 +19,8 @@
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.draw.clipToBounds
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.InternalTextApi
@@ -54,7 +55,7 @@
 @Composable
 @OptIn(InternalTextApi::class)
 private fun TextFieldWithMaxLines(str: String? = null, maxLines: Int) {
-    val state = savedInstanceState { str ?: "abc ".repeat(20) }
+    val state = rememberSaveable { mutableStateOf(str ?: "abc ".repeat(20)) }
     BasicTextField(
         modifier = demoTextFieldModifiers.clipToBounds(),
         value = state.value,
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldTrickyUseCase.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldTrickyUseCase.kt
index d95e21e..ebc4ebe 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldTrickyUseCase.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeInputFieldTrickyUseCase.kt
@@ -20,7 +20,8 @@
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.unit.sp
@@ -42,7 +43,7 @@
 
 @Composable
 private fun RejectNonDigits() {
-    val state = savedInstanceState { "" }
+    val state = rememberSaveable { mutableStateOf("") }
     BasicTextField(
         modifier = demoTextFieldModifiers,
         value = state.value,
@@ -58,7 +59,7 @@
 
 @Composable
 private fun RejectComposition() {
-    val state = savedInstanceState { "" }
+    val state = rememberSaveable { mutableStateOf(({ "" })()) }
     BasicTextField(
         modifier = demoTextFieldModifiers,
         value = state.value,
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeVariousInputField.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeVariousInputField.kt
index abc507b..48bf8cd 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeVariousInputField.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeVariousInputField.kt
@@ -22,12 +22,13 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material.Text
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.AnnotatedString
@@ -252,7 +253,7 @@
     onValueChange: (String, String) -> String = { _, new -> new },
     visualTransformation: VisualTransformation
 ) {
-    val state = savedInstanceState { "" }
+    val state = rememberSaveable { mutableStateOf("") }
     BasicTextField(
         modifier = demoTextFieldModifiers,
         value = state.value,
@@ -272,7 +273,7 @@
 
 @Composable
 private fun HintEditText(content: @Composable () -> Unit) {
-    val state = savedInstanceState { "" }
+    val state = rememberSaveable { mutableStateOf("") }
 
     Box(demoTextFieldModifiers) {
         BasicTextField(
@@ -289,7 +290,9 @@
 
 @Composable
 private fun InteractionStateTextField() {
-    val state = savedInstanceState(saver = TextFieldValue.Saver) { TextFieldValue() }
+    val state = rememberSaveable(stateSaver = TextFieldValue.Saver) {
+        mutableStateOf(TextFieldValue())
+    }
     val interactionState = remember { InteractionState() }
 
     Column(demoTextFieldModifiers) {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/KeyboardSingleLineDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/KeyboardSingleLineDemo.kt
index 4783888..1bc5c66 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/KeyboardSingleLineDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/KeyboardSingleLineDemo.kt
@@ -24,7 +24,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
@@ -124,7 +124,9 @@
 )
 private fun MyTextField(data: ImeOptionsData) {
     val controller = remember { mutableStateOf<SoftwareKeyboardController?>(null) }
-    val state = savedInstanceState(saver = TextFieldValue.Saver) { TextFieldValue() }
+    val state = rememberSaveable(stateSaver = TextFieldValue.Saver) {
+        mutableStateOf(TextFieldValue())
+    }
     BasicTextField(
         modifier = demoTextFieldModifiers.defaultMinSizeConstraints(100.dp),
         value = state.value,
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TailFollowingTextField.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TailFollowingTextField.kt
index 88a72be..f013764 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TailFollowingTextField.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TailFollowingTextField.kt
@@ -21,7 +21,8 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clipToBounds
 import androidx.compose.ui.layout.Layout
@@ -34,7 +35,9 @@
 @Composable
 fun TailFollowingTextFieldDemo() {
     Column {
-        val hstate = savedInstanceState { "abc def ghi jkl mno pqr stu vwx yz" }
+        val hstate = rememberSaveable {
+            mutableStateOf("abc def ghi jkl mno pqr stu vwx yz")
+        }
         HorizontalTailFollowingTextField(
             value = hstate.value,
             onValueChange = { hstate.value = it },
@@ -44,7 +47,9 @@
                 .clipToBounds()
         )
 
-        val vstate = savedInstanceState { "a\nb\nc\nd\ne\nf\ng\nh" }
+        val vstate = rememberSaveable {
+            mutableStateOf("a\nb\nc\nd\ne\nf\ng\nh")
+        }
         VerticalTailFollowintTextField(
             value = vstate.value,
             onValueChange = { vstate.value = it },
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt
index d1cc274..de4e98c3 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt
@@ -25,14 +25,15 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Text
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.material.Icon
+import androidx.compose.material.Text
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.MailOutline
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -42,7 +43,9 @@
 @Sampled
 @Composable
 fun BasicTextFieldSample() {
-    var value by savedInstanceState(saver = TextFieldValue.Saver) { TextFieldValue() }
+    var value by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+        mutableStateOf(TextFieldValue())
+    }
     BasicTextField(
         value = value,
         onValueChange = {
@@ -56,7 +59,7 @@
 @Sampled
 @Composable
 fun BasicTextFieldWithStringSample() {
-    var value by savedInstanceState { "initial value" }
+    var value by rememberSaveable { mutableStateOf("initial value") }
     BasicTextField(
         value = value,
         onValueChange = {
@@ -71,7 +74,7 @@
 @Composable
 @OptIn(ExperimentalFoundationApi::class)
 fun PlaceholderBasicTextFieldSample() {
-    var value by savedInstanceState { "initial value" }
+    var value by rememberSaveable { mutableStateOf("initial value") }
     Box {
         BasicTextField(
             value = value,
@@ -87,7 +90,7 @@
 @Composable
 @OptIn(ExperimentalFoundationApi::class)
 fun TextFieldWithIconSample() {
-    var value by savedInstanceState { "initial value" }
+    var value by rememberSaveable { mutableStateOf("initial value") }
     BasicTextField(
         value = value,
         onValueChange = { value = it },
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TextFieldSample.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TextFieldSample.kt
index bb60c2e..a5570d9 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TextFieldSample.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TextFieldSample.kt
@@ -23,7 +23,8 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.text.input.TextFieldValue
 
@@ -32,7 +33,9 @@
 @OptIn(ExperimentalFoundationApi::class)
 @Suppress("DEPRECATION")
 fun TextFieldSample() {
-    var value by savedInstanceState(saver = TextFieldValue.Saver) { TextFieldValue() }
+    var value by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+        mutableStateOf(TextFieldValue())
+    }
     BaseTextField(
         value = value,
         onValueChange = { value = it }
@@ -44,7 +47,9 @@
 @OptIn(ExperimentalFoundationApi::class)
 @Suppress("DEPRECATION")
 fun PlaceholderTextFieldSample() {
-    val state = savedInstanceState(saver = TextFieldValue.Saver) { TextFieldValue() }
+    val state = rememberSaveable(stateSaver = TextFieldValue.Saver) {
+        mutableStateOf(TextFieldValue())
+    }
     Box {
         BaseTextField(
             value = state.value,
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ProgressSemanticsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ProgressSemanticsTest.kt
index 2992dee..10ba7c1 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ProgressSemanticsTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ProgressSemanticsTest.kt
@@ -23,6 +23,10 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.ProgressBarRangeInfo
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.getOrNull
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertRangeInfoEquals
 import androidx.compose.ui.test.assertValueEquals
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -93,5 +97,13 @@
 
         rule.onNodeWithTag(tag)
             .assertValueEquals(Strings.InProgress)
+
+        rule.onNodeWithTag(tag)
+            .assert(
+                SemanticsMatcher("progress is ProgressBarRangeInfo.Indeterminate") {
+                    val progress = it.config.getOrNull(SemanticsProperties.ProgressBarRangeInfo)
+                    progress === ProgressBarRangeInfo.Indeterminate
+                }
+            )
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
index ca6be5a..fd8a3f6 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
@@ -41,7 +41,7 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
@@ -83,6 +83,7 @@
 import androidx.compose.ui.text.input.CommitTextCommand
 import androidx.compose.ui.text.input.EditCommand
 import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.TextFieldValue.Companion.Saver
 import androidx.compose.ui.text.input.TextInputService
@@ -376,7 +377,7 @@
 
         val restorationTester = StateRestorationTester(rule)
         restorationTester.setContent {
-            state = savedInstanceState(saver = Saver) { TextFieldValue() }
+            state = rememberSaveable(stateSaver = Saver) { mutableStateOf(TextFieldValue()) }
         }
 
         rule.runOnIdle {
@@ -590,6 +591,22 @@
         }
     }
 
+    @Test
+    fun semantics_passwordTextField_noCopyCutActions() {
+        rule.setContent {
+            BasicTextField(
+                modifier = Modifier.testTag(Tag),
+                value = TextFieldValue("Hello", TextRange(0, 3)),
+                onValueChange = {},
+                visualTransformation = PasswordVisualTransformation()
+            )
+        }
+
+        rule.onNodeWithTag(Tag)
+            .assert(SemanticsMatcher.keyNotDefined(SemanticsActions.CopyText))
+            .assert(SemanticsMatcher.keyNotDefined(SemanticsActions.CutText))
+    }
+
     @LargeTest
     @Test
     fun semantics_longClick() {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/DarkTheme.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/DarkTheme.kt
index 2b0490d..becda0f 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/DarkTheme.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/DarkTheme.kt
@@ -18,7 +18,7 @@
 
 import android.content.res.Configuration
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposableContract
+import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.ui.platform.AmbientConfiguration
 
 /**
@@ -43,7 +43,7 @@
  * @return `true` if the system is considered to be in 'dark theme'.
  */
 @Composable
-@ComposableContract(readonly = true)
+@ReadOnlyComposable
 fun isSystemInDarkTheme(): Boolean {
     val uiMode = AmbientConfiguration.current.uiMode
     return (uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingDecaySpec.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingDecaySpec.kt
index 8ef4e52..d07bf8b 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingDecaySpec.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingDecaySpec.kt
@@ -23,12 +23,6 @@
 import androidx.compose.ui.unit.Density
 import kotlin.math.sign
 
-@Deprecated(
-    "AndroidFlingDecaySpec has been renamed to FloatAndroidFlingDecaySpec",
-    replaceWith = ReplaceWith("FloatAndroidFlingDecaySpec")
-)
-typealias AndroidFlingDecaySpec = FloatAndroidFlingDecaySpec
-
 /**
  * A native Android fling curve decay.
  *
@@ -46,17 +40,32 @@
     private fun flingDistance(startVelocity: Float): Float =
         flingCalculator.flingDistance(startVelocity) * sign(startVelocity)
 
-    override fun getTarget(start: Float, startVelocity: Float): Float =
-        start + flingDistance(startVelocity)
+    override fun getTargetValue(initialValue: Float, initialVelocity: Float): Float =
+        initialValue + flingDistance(initialVelocity)
 
-    override fun getValue(playTime: Long, start: Float, startVelocity: Float): Float =
-        start + flingCalculator.flingInfo(startVelocity).position(playTime)
+    @Suppress("MethodNameUnits")
+    override fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        initialVelocity: Float
+    ): Float {
+        val playTimeMillis = playTimeNanos / 1_000_000L
+        return initialValue + flingCalculator.flingInfo(initialVelocity).position(playTimeMillis)
+    }
 
-    override fun getDurationMillis(start: Float, startVelocity: Float): Long =
-        flingCalculator.flingDuration(startVelocity)
+    @Suppress("MethodNameUnits")
+    override fun getDurationNanos(initialValue: Float, initialVelocity: Float): Long =
+        flingCalculator.flingDuration(initialVelocity) * 1_000_000L
 
-    override fun getVelocity(playTime: Long, start: Float, startVelocity: Float): Float =
-        flingCalculator.flingInfo(startVelocity).velocity(playTime)
+    @Suppress("MethodNameUnits")
+    override fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        initialVelocity: Float
+    ): Float {
+        val playTimeMillis = playTimeNanos / 1_000_000L
+        return flingCalculator.flingInfo(initialVelocity).velocity(playTimeMillis)
+    }
 }
 
 /**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt
index 2f8d50d..a97afc2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt
@@ -17,7 +17,6 @@
 package androidx.compose.foundation
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.layout.Layout
@@ -184,7 +183,7 @@
     // Explicitly use a simple Layout implementation here as Spacer squashes any non fixed
     // constraint with zero
     Layout(
-        emptyContent(),
+        {},
         modifier.then(semantics).clipToBounds().paint(
             painter,
             alignment = alignment,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt
index 2c78a98..c22aed6 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt
@@ -78,5 +78,8 @@
  */
 @Stable
 fun Modifier.progressSemantics(): Modifier {
-    return semantics { stateDescription = Strings.InProgress }
+    return semantics {
+        stateDescription = Strings.InProgress
+        progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt
index 5eb7e0d..c1763ec 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt
@@ -20,7 +20,7 @@
 import androidx.compose.animation.core.VectorConverter
 import androidx.compose.animation.core.spring
 import androidx.compose.foundation.gestures.Scrollable
-import androidx.compose.runtime.withFrameMillis
+import androidx.compose.runtime.withFrameNanos
 
 /**
  * Smooth scroll by [value] pixels.
@@ -44,16 +44,16 @@
     var previousValue = 0f
 
     scroll {
-        val startTimeMillis = withFrameMillis { it }
+        val startTimeNanos = withFrameNanos { it }
         do {
-            val finished = withFrameMillis { frameTimeMillis ->
+            val finished = withFrameNanos { frameTimeNanos ->
                 val newValue = conv.convertFromVector(
-                    animSpec.getValue(
-                        playTime = frameTimeMillis - startTimeMillis,
-                        start = zeroVector,
-                        end = targetVector,
+                    animSpec.getValueFromNanos(
+                        playTimeNanos = frameTimeNanos - startTimeNanos,
+                        initialValue = zeroVector,
+                        targetValue = targetVector,
                         // TODO: figure out if/how we should incorporate existing velocity
-                        startVelocity = zeroVector
+                        initialVelocity = zeroVector
                     )
                 )
                 val delta = newValue - previousValue
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt
index 88fc1bf..e710860 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt
@@ -17,19 +17,14 @@
 package androidx.compose.foundation.lazy
 
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxWithConstraints
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.constrainHeight
-import androidx.compose.ui.unit.constrainWidth
 import androidx.compose.ui.unit.dp
 
 /**
@@ -81,6 +76,7 @@
 /**
  * This class describes how cells form columns in vertical grids or rows in horizontal grids.
  */
+@ExperimentalFoundationApi
 sealed class GridCells {
     /**
      * Combines cells with fixed number rows or columns.
@@ -88,6 +84,7 @@
      * For example, for the vertical [LazyVerticalGrid] Fixed(3) would mean that there are 3 columns 1/3
      * of the parent wide.
      */
+    @ExperimentalFoundationApi
     class Fixed(val count: Int) : GridCells()
 
     /**
@@ -99,12 +96,14 @@
      * many columns as possible and every column will be at least 20.dp and all the columns will
      * have equal width. If the screen is 88.dp wide then there will be 4 columns 22.dp each.
      */
+    @ExperimentalFoundationApi
     class Adaptive(val minSize: Dp) : GridCells()
 }
 
 /**
  * Receiver scope which is used by [LazyVerticalGrid].
  */
+@ExperimentalFoundationApi
 interface LazyGridScope {
     /**
      * Adds a single item to the scope.
@@ -128,6 +127,7 @@
  * @param items the data list
  * @param itemContent the content displayed by a single item
  */
+@ExperimentalFoundationApi
 inline fun <T> LazyGridScope.items(
     items: List<T>,
     crossinline itemContent: @Composable LazyItemScope.(item: T) -> Unit
@@ -141,6 +141,7 @@
  * @param items the data list
  * @param itemContent the content displayed by a single item
  */
+@ExperimentalFoundationApi
 inline fun <T> LazyGridScope.itemsIndexed(
     items: List<T>,
     crossinline itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
@@ -154,6 +155,7 @@
  * @param items the data array
  * @param itemContent the content displayed by a single item
  */
+@ExperimentalFoundationApi
 inline fun <T> LazyGridScope.items(
     items: Array<T>,
     crossinline itemContent: @Composable LazyItemScope.(item: T) -> Unit
@@ -167,6 +169,7 @@
  * @param items the data array
  * @param itemContent the content displayed by a single item
  */
+@ExperimentalFoundationApi
 inline fun <T> LazyGridScope.itemsIndexed(
     items: Array<T>,
     crossinline itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
@@ -175,6 +178,7 @@
 }
 
 @Composable
+@ExperimentalFoundationApi
 private fun FixedLazyGrid(
     nColumns: Int,
     modifier: Modifier = Modifier,
@@ -183,25 +187,21 @@
     scope: LazyGridScopeImpl
 ) {
     val rows = (scope.totalSize + nColumns - 1) / nColumns
-    LazyList(
-        itemsCount = rows,
+    LazyColumn(
         modifier = modifier,
         state = state,
-        contentPadding = contentPadding,
-        isVertical = true,
-        horizontalAlignment = Alignment.Start,
-        verticalArrangement = Arrangement.Top,
-        reverseLayout = false
-    ) { rowIndex ->
-        @Composable {
+        contentPadding = contentPadding
+    ) {
+        items(rows) { rowIndex ->
             Row {
                 for (columnIndex in 0 until nColumns) {
                     val itemIndex = rowIndex * nColumns + columnIndex
                     if (itemIndex < scope.totalSize) {
-                        GridCellBox(
-                            modifier = Modifier.weight(1f, fill = true)
+                        Box(
+                            modifier = Modifier.weight(1f, fill = true),
+                            propagateMinConstraints = true
                         ) {
-                            scope.contentFor(itemIndex, this@LazyList).invoke()
+                            scope.contentFor(itemIndex, this@items).invoke()
                         }
                     } else {
                         Spacer(Modifier.weight(1f, fill = true))
@@ -212,24 +212,7 @@
     }
 }
 
-/**
- * TODO: Remove when the Box component supports fixed constraints.
- */
-@Composable
-private fun GridCellBox(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
-    Layout(content, modifier) { measurables, constraints ->
-        val placeables = measurables.map { it.measure(constraints) }
-        val size = placeables.fold(IntSize.Zero) { size, item ->
-            IntSize(maxOf(size.width, item.width), maxOf(size.height, item.height))
-        }
-        layout(constraints.constrainWidth(size.width), constraints.constrainHeight(size.height)) {
-            placeables.forEach {
-                it.place(0, 0)
-            }
-        }
-    }
-}
-
+@ExperimentalFoundationApi
 internal class LazyGridScopeImpl : LazyGridScope {
     private val intervals = IntervalList<LazyItemScope.(Int) -> (@Composable () -> Unit)>()
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
index f847c51..9ff8fdb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
@@ -22,7 +22,6 @@
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.DisposableEffectResult
 import androidx.compose.runtime.DisposableEffectScope
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -148,7 +147,7 @@
 
     Layout(
         content = if (inlineComposables.isEmpty()) {
-            emptyContent()
+            {}
         } else {
             { InlineChildren(text, inlineComposables) }
         },
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
index c7801fc..b4fed75 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
@@ -26,7 +26,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.currentRecomposeScope
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -62,6 +61,7 @@
 import androidx.compose.ui.semantics.imeAction
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.onLongClick
+import androidx.compose.ui.semantics.password
 import androidx.compose.ui.semantics.pasteText
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.setSelection
@@ -82,6 +82,7 @@
 import androidx.compose.ui.text.input.ImeOptions
 import androidx.compose.ui.text.input.NO_SESSION
 import androidx.compose.ui.text.input.OffsetMapping
+import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.TextInputService
 import androidx.compose.ui.text.input.VisualTransformation
@@ -240,6 +241,7 @@
 
     val manager = remember { TextFieldSelectionManager() }
     manager.offsetMapping = offsetMapping
+    manager.visualTransformation = visualTransformation
     manager.onValueChange = onValueChangeWrapper
     manager.state = state
     manager.value = value
@@ -353,12 +355,14 @@
         state.layoutResult?.innerTextFieldCoordinates = it
     }
 
+    val isPassword = visualTransformation is PasswordVisualTransformation
     val semanticsModifier = Modifier.semantics {
         // focused semantics are handled by Modifier.focusable()
         this.imeAction = imeOptions.imeAction
         this.text = value.annotatedString
         this.textSelectionRange = value.selection
         if (!enabled) this.disabled()
+        if (isPassword) this.password()
         getTextLayoutResult {
             if (state.layoutResult != null) {
                 it.add(state.layoutResult!!.value)
@@ -386,7 +390,12 @@
                 } else {
                     manager.enterSelectionMode()
                 }
-                onValueChangeWrapper(TextFieldValue(value.annotatedString, TextRange(start, end)))
+                onValueChangeWrapper(
+                    TextFieldValue(
+                        value.annotatedString,
+                        TextRange(start, end)
+                    )
+                )
                 true
             } else {
                 manager.exitSelectionMode()
@@ -403,7 +412,7 @@
             manager.enterSelectionMode()
             true
         }
-        if (!value.selection.collapsed) {
+        if (!value.selection.collapsed && !isPassword) {
             copyText {
                 manager.copy()
                 true
@@ -460,7 +469,7 @@
                 .textFieldKeyboardModifier(manager)
 
             SimpleLayout(coreTextFieldModifier) {
-                Layout(emptyContent()) { _, constraints ->
+                Layout({ }) { _, constraints ->
                     TextFieldDelegate.layout(
                         state.textDelegate,
                         constraints,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
index 5a0f2f6..c80f96e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
@@ -42,7 +42,9 @@
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.input.OffsetMapping
+import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.text.input.getSelectedText
 import androidx.compose.ui.text.input.getTextAfterSelection
 import androidx.compose.ui.text.input.getTextBeforeSelection
@@ -78,6 +80,12 @@
     internal var value: TextFieldValue = TextFieldValue()
 
     /**
+     * Visual transformation of the text field's text. Used to check if certain toolbar options
+     * are permitted. For example, 'cut' will not be available is it is password transformation.
+     */
+    internal var visualTransformation: VisualTransformation = VisualTransformation.None
+
+    /**
      * [ClipboardManager] to perform clipboard features.
      */
     internal var clipboardManager: ClipboardManager? = null
@@ -433,21 +441,22 @@
      * the copy, paste and cut method as callbacks when "copy", "cut" or "paste" is clicked.
      */
     internal fun showSelectionToolbar() {
-        val copy: (() -> Unit)? = if (!value.selection.collapsed) {
+        val isPassword = visualTransformation is PasswordVisualTransformation
+        val copy: (() -> Unit)? = if (!value.selection.collapsed && !isPassword) {
             {
                 copy()
                 hideSelectionToolbar()
             }
         } else null
 
-        val cut: (() -> Unit)? = if (!value.selection.collapsed) {
+        val cut: (() -> Unit)? = if (!value.selection.collapsed && editable && !isPassword) {
             {
                 cut()
                 hideSelectionToolbar()
             }
         } else null
 
-        val paste: (() -> Unit)? = if (clipboardManager?.getText() != null) {
+        val paste: (() -> Unit)? = if (editable && clipboardManager?.getText() != null) {
             {
                 paste()
                 hideSelectionToolbar()
@@ -463,8 +472,8 @@
         textToolbar?.showMenu(
             rect = getContentRect(),
             onCopyRequested = copy,
-            onPasteRequested = if (editable) paste else null,
-            onCutRequested = if (editable) cut else null,
+            onPasteRequested = paste,
+            onCutRequested = cut,
             onSelectAllRequested = selectAll
         )
     }
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/animation/DesktopFlingDecaySpec.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/animation/DesktopFlingDecaySpec.kt
index 06a4d1b..f55f526 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/animation/DesktopFlingDecaySpec.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/animation/DesktopFlingDecaySpec.kt
@@ -34,15 +34,30 @@
     private fun flingDistance(startVelocity: Float): Float =
         flingCalculator.flingDistance(startVelocity) * sign(startVelocity)
 
-    override fun getTarget(start: Float, startVelocity: Float): Float =
-        start + flingDistance(startVelocity)
+    override fun getTargetValue(initialValue: Float, initialVelocity: Float): Float =
+        initialValue + flingDistance(initialVelocity)
 
-    override fun getValue(playTime: Long, start: Float, startVelocity: Float): Float =
-        start + flingCalculator.flingInfo(startVelocity).position(playTime)
+    @Suppress("MethodNameUnits")
+    override fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        initialVelocity: Float
+    ): Float {
+        val playTimeMillis = playTimeNanos / 1_000_000L
+        return initialValue + flingCalculator.flingInfo(initialVelocity).position(playTimeMillis)
+    }
 
-    override fun getDurationMillis(start: Float, startVelocity: Float): Long =
-        flingCalculator.flingDuration(startVelocity)
+    @Suppress("MethodNameUnits")
+    override fun getDurationNanos(initialValue: Float, initialVelocity: Float): Long =
+        flingCalculator.flingDuration(initialVelocity)
 
-    override fun getVelocity(playTime: Long, start: Float, startVelocity: Float): Float =
-        flingCalculator.flingInfo(startVelocity).velocity(playTime)
+    @Suppress("MethodNameUnits")
+    override fun getVelocityFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        initialVelocity: Float
+    ): Float {
+        val playTimeMillis = playTimeNanos / 1_000_000L
+        return flingCalculator.flingInfo(initialVelocity).velocity(playTimeMillis)
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
index a29320c..152d884 100644
--- a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
+++ b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
@@ -54,7 +54,6 @@
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.yield
 import org.jetbrains.skija.Surface
 import org.junit.Assert.assertEquals
 import org.junit.Ignore
@@ -68,7 +67,7 @@
     val rule = createComposeRule()
 
     // don't inline, surface controls canvas life time
-    private val surface = Surface.makeRasterN32Premul(100, 100)!!
+    private val surface = Surface.makeRasterN32Premul(100, 100)
     private val canvas = surface.canvas
 
     @Test
@@ -82,7 +81,7 @@
             rule.onNodeWithTag("scrollbar").performGesture {
                 swipe(start = Offset(0f, 25f), end = Offset(0f, 50f))
             }
-            onFrame()
+            rule.awaitIdle()
             rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-50.dp)
         }
     }
@@ -98,13 +97,13 @@
             rule.onNodeWithTag("scrollbar").performGesture {
                 swipe(start = Offset(0f, 25f), end = Offset(0f, 500f))
             }
-            onFrame()
+            rule.awaitIdle()
             rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-100.dp)
 
             rule.onNodeWithTag("scrollbar").performGesture {
                 swipe(start = Offset(0f, 99f), end = Offset(0f, -500f))
             }
-            onFrame()
+            rule.awaitIdle()
             rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(0.dp)
         }
     }
@@ -120,7 +119,7 @@
             rule.onNodeWithTag("scrollbar").performGesture {
                 swipe(start = Offset(10f, 25f), end = Offset(0f, 50f))
             }
-            onFrame()
+            rule.awaitIdle()
             rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(0.dp)
         }
     }
@@ -136,7 +135,7 @@
             rule.awaitIdle()
 
             rule.performMouseScroll(0, 25, 1f)
-            onFrame()
+            rule.awaitIdle()
             rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-10.dp)
         }
     }
@@ -152,7 +151,7 @@
             rule.awaitIdle()
 
             rule.performMouseScroll(0, 99, 1f)
-            onFrame()
+            rule.awaitIdle()
             rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-10.dp)
         }
     }
@@ -178,13 +177,13 @@
             rule.awaitIdle()
 
             rule.performMouseScroll(20, 25, 10f)
-            onFrame()
+            rule.awaitIdle()
             rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-100.dp)
 
             rule.onNodeWithTag("scrollbar").performGesture {
                 swipe(start = Offset(0f, 99f), end = Offset(0f, -500f))
             }
-            onFrame()
+            rule.awaitIdle()
             rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(0.dp)
         }
     }
@@ -202,7 +201,7 @@
             }
 
             tryUntilSucceeded {
-                onFrame()
+                rule.awaitIdle()
                 rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-100.dp)
             }
         }
@@ -221,7 +220,7 @@
             }
 
             tryUntilSucceeded {
-                onFrame()
+                rule.awaitIdle()
                 rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-300.dp)
             }
         }
@@ -243,15 +242,15 @@
                     }
                 }
             }
-            onFrame()
+            rule.awaitIdle()
 
             isContentVisible.value = true
-            onFrame()
+            rule.awaitIdle()
 
             rule.onNodeWithTag("scrollbar").performGesture {
                 swipe(start = Offset(0f, 25f), end = Offset(0f, 500f))
             }
-            onFrame()
+            rule.awaitIdle()
             rule.onNodeWithTag("box0").assertTopPositionInRootIsEqualTo(-100.dp)
         }
     }
@@ -278,7 +277,7 @@
             rule.onNodeWithTag("scrollbar").performGesture {
                 swipe(start = Offset(0f, 0f), end = Offset(0f, 11f), durationMillis = 1)
             }
-            onFrame()
+            rule.awaitIdle()
             assertEquals(2, state.firstVisibleItemIndex)
             assertEquals(4, state.firstVisibleItemScrollOffset)
         }
@@ -306,7 +305,7 @@
             rule.onNodeWithTag("scrollbar").performGesture {
                 swipe(start = Offset(0f, 0f), end = Offset(0f, 26f), durationMillis = 1)
             }
-            onFrame()
+            rule.awaitIdle()
             assertEquals(5, state.firstVisibleItemIndex)
             assertEquals(4, state.firstVisibleItemScrollOffset)
         }
@@ -334,14 +333,14 @@
             rule.onNodeWithTag("scrollbar").performGesture {
                 swipe(start = Offset(0f, 0f), end = Offset(0f, 10000f), durationMillis = 1)
             }
-            onFrame()
+            rule.awaitIdle()
             assertEquals(15, state.firstVisibleItemIndex)
             assertEquals(0, state.firstVisibleItemScrollOffset)
 
             rule.onNodeWithTag("scrollbar").performGesture {
                 swipe(start = Offset(0f, 99f), end = Offset(0f, -10000f), durationMillis = 1)
             }
-            onFrame()
+            rule.awaitIdle()
             assertEquals(0, state.firstVisibleItemIndex)
             assertEquals(0, state.firstVisibleItemScrollOffset)
         }
@@ -358,16 +357,8 @@
         }
     }
 
-    // TODO(demin): move to DesktopComposeTestRule?
-    private suspend fun onFrame() {
-        // TODO(demin): probably we don't need `yield` after we fix https://github.com/JetBrains/compose-jb/issues/137
-        yield()
-        (rule as DesktopComposeTestRule).owners?.onFrame(canvas, 100, 100, 0)
-        rule.awaitIdle()
-    }
-
     private fun ComposeTestRule.performMouseScroll(x: Int, y: Int, delta: Float) {
-        (this as DesktopComposeTestRule).owners!!.onMouseScroll(
+        (this as DesktopComposeTestRule).window.onMouseScroll(
             x, y, MouseScrollEvent(MouseScrollUnit.Line(delta), Orientation.Vertical)
         )
     }
diff --git a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/gestures/DesktopScrollableTest.kt b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/gestures/DesktopScrollableTest.kt
index 78b9ef8..b954f4b 100644
--- a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/gestures/DesktopScrollableTest.kt
+++ b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/gestures/DesktopScrollableTest.kt
@@ -27,10 +27,10 @@
 import androidx.compose.ui.input.mouse.MouseScrollEvent
 import androidx.compose.ui.input.mouse.MouseScrollUnit
 import androidx.compose.ui.platform.DesktopPlatform
+import androidx.compose.ui.platform.TestComposeWindow
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.test.TestComposeWindow
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -40,7 +40,15 @@
 @RunWith(JUnit4::class)
 class DesktopScrollableTest {
     private val density = 2f
-    private val window = TestComposeWindow(width = 100, height = 100, density = Density(density))
+
+    private fun window(
+        platform: DesktopPlatform
+    ) = TestComposeWindow(
+        width = 100,
+        height = 100,
+        density = Density(density),
+        desktopPlatform = platform
+    )
 
     private fun scrollLineLinux(bounds: Dp) = sqrt(bounds.value * density)
     private fun scrollLineWindows(bounds: Dp) = bounds.value * density / 20f
@@ -49,8 +57,7 @@
 
     @Test
     fun `linux, scroll vertical`() {
-        window.desktopPlatform = DesktopPlatform.Linux
-
+        val window = window(platform = DesktopPlatform.Linux)
         val context = TestColumn()
 
         window.setContent {
@@ -64,7 +71,7 @@
             )
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(3f), Orientation.Vertical)
@@ -72,7 +79,7 @@
 
         assertThat(context.offset).isWithin(0.1f).of(-3 * scrollLineLinux(20.dp))
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(3f), Orientation.Vertical)
@@ -83,8 +90,7 @@
 
     @Test
     fun `windows, scroll vertical`() {
-        window.desktopPlatform = DesktopPlatform.Windows
-
+        val window = window(platform = DesktopPlatform.Windows)
         val context = TestColumn()
 
         window.setContent {
@@ -98,7 +104,7 @@
             )
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(-2f), Orientation.Vertical)
@@ -106,7 +112,7 @@
 
         assertThat(context.offset).isWithin(0.1f).of(2 * scrollLineWindows(20.dp))
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(4f), Orientation.Vertical)
@@ -117,8 +123,7 @@
 
     @Test
     fun `windows, scroll one page vertical`() {
-        window.desktopPlatform = DesktopPlatform.Windows
-
+        val window = window(platform = DesktopPlatform.Windows)
         val context = TestColumn()
 
         window.setContent {
@@ -132,7 +137,7 @@
             )
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Page(1f), Orientation.Vertical)
@@ -143,8 +148,7 @@
 
     @Test
     fun `macOS, scroll vertical`() {
-        window.desktopPlatform = DesktopPlatform.MacOS
-
+        val window = window(platform = DesktopPlatform.MacOS)
         val context = TestColumn()
 
         window.setContent {
@@ -158,7 +162,7 @@
             )
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(-5.5f), Orientation.Vertical)
@@ -169,8 +173,7 @@
 
     @Test
     fun `scroll with different orientation`() {
-        window.desktopPlatform = DesktopPlatform.Linux
-
+        val window = window(platform = DesktopPlatform.Linux)
         val column = TestColumn()
 
         window.setContent {
@@ -184,7 +187,7 @@
             )
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(3f), Orientation.Horizontal)
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
index c42210e9..6663291 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
@@ -33,6 +33,7 @@
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.OffsetMapping
+import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.style.ResolvedTextDirection
 import androidx.compose.ui.text.style.TextOverflow
@@ -482,6 +483,17 @@
     }
 
     @Test
+    fun showSelectionToolbar_passwordTextField_not_show_copy_cut() {
+        manager.visualTransformation = PasswordVisualTransformation()
+        whenever(clipboardManager.getText()).thenReturn(AnnotatedString(text))
+        manager.value = TextFieldValue(text, TextRange(0, 5))
+
+        manager.showSelectionToolbar()
+
+        verify(textToolbar, times(1)).showMenu(any(), isNull(), any(), isNull(), any())
+    }
+
+    @Test
     fun isTextChanged_text_changed_return_true() {
         manager.touchSelectionObserver.onLongPress(dragBeginPosition)
         manager.value = TextFieldValue(text + text)
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt
index a61b285..696e0be 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt
@@ -29,7 +29,6 @@
 import androidx.compose.foundation.lazy.itemsIndexed
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.testutils.assertNoPendingChanges
 import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
 import androidx.compose.testutils.doFramesUntilNoChangesPending
@@ -237,7 +236,7 @@
     @Composable
     fun RemeasurableItem() {
         Layout(
-            emptyContent(),
+            {},
             modifier = object : RemeasurementModifier {
                 override fun onRemeasurementAvailable(remeasurement: Remeasurement) {
                     this@ListRemeasureTestCase.remeasurement = remeasurement
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/AnimationBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/AnimationBenchmark.kt
index 75a6481..3911015 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/AnimationBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/AnimationBenchmark.kt
@@ -51,7 +51,7 @@
 
         benchmarkRule.measureRepeated {
             for (time in 0..1000 step 20) {
-                anim.getValue(time.toLong(), start, end, start)
+                anim.getValueFromNanos(time * 1_000_000L, start, end, start)
             }
         }
     }
@@ -65,7 +65,7 @@
 
         benchmarkRule.measureRepeated {
             for (time in 0..1000 step 20) {
-                fixedAnimation.getValue(time.toLong())
+                fixedAnimation.getValueFromNanos(time * 1_000_000L)
             }
         }
     }
@@ -78,7 +78,7 @@
 
         benchmarkRule.measureRepeated {
             for (time in 0..1000 step 20) {
-                anim.getValue(time.toLong(), start, end, start)
+                anim.getValueFromNanos(time * 1_000_000L, start, end, start)
             }
         }
     }
@@ -92,7 +92,7 @@
 
         benchmarkRule.measureRepeated {
             for (time in 0..1000 step 20) {
-                fixedAnimation.getValue(time.toLong())
+                fixedAnimation.getValueFromNanos(time * 1_000_000L)
             }
         }
     }
@@ -105,7 +105,7 @@
 
         benchmarkRule.measureRepeated {
             for (time in 0..1) {
-                anim.getValue(time.toLong(), start, end, start)
+                anim.getValueFromNanos(time * 1_000_000L, start, end, start)
             }
         }
     }
@@ -119,7 +119,7 @@
 
         benchmarkRule.measureRepeated {
             for (time in 0..1) {
-                fixedAnimation.getValue(time.toLong())
+                fixedAnimation.getValueFromNanos(time * 1_000_000L)
             }
         }
     }
@@ -138,7 +138,7 @@
 
         benchmarkRule.measureRepeated {
             for (time in 0..1000 step 20) {
-                anim.getValue(time.toLong(), start, end, start)
+                anim.getValueFromNanos(time * 1_000_000L, start, end, start)
             }
         }
     }
@@ -158,7 +158,7 @@
 
         benchmarkRule.measureRepeated {
             for (time in 0..1000 step 20) {
-                fixedAnimation.getValue(time.toLong())
+                fixedAnimation.getValueFromNanos(time * 1_000_000L)
             }
         }
     }
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
index f84c765..4136fb9 100644
--- a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
+++ b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
@@ -45,7 +45,8 @@
 import androidx.compose.material.icons.filled.Settings
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+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
@@ -68,7 +69,7 @@
 ) {
     val navigationIcon = (@Composable { AppBarIcons.Back(onNavigateUp) }).takeIf { canNavigateUp }
 
-    var filterText by savedInstanceState { "" }
+    var filterText by rememberSaveable { mutableStateOf("") }
 
     Scaffold(
         topBar = {
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
index badd5b5..589bddd 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
@@ -66,7 +66,7 @@
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.saveable.listSaver
 import androidx.compose.runtime.saveable.mapSaver
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -367,7 +367,7 @@
 private object InteropSnippet15 {
     @Composable
     fun MyExample() {
-        var selectedId by savedInstanceState<String?> { null }
+        var selectedId by rememberSaveable { mutableStateOf<String?>(null) }
         /*...*/
     }
 }
@@ -380,7 +380,7 @@
 
     @Composable
     fun MyExample() {
-        var selectedCity = savedInstanceState { City("Madrid", "Spain") }
+        var selectedCity = rememberSaveable { mutableStateOf(City("Madrid", "Spain")) }
     }
      */
 }
@@ -399,7 +399,9 @@
 
     @Composable
     fun MyExample() {
-        var selectedCity = savedInstanceState(CitySaver) { City("Madrid", "Spain") }
+        var selectedCity = rememberSaveable(stateSaver = CitySaver) {
+            mutableStateOf(City("Madrid", "Spain"))
+        }
     }
 }
 
@@ -413,7 +415,9 @@
 
     @Composable
     fun MyExample() {
-        var selectedCity = savedInstanceState(CitySaver) { City("Madrid", "Spain") }
+        var selectedCity = rememberSaveable(stateSaver = CitySaver) {
+            mutableStateOf(City("Madrid", "Spain"))
+        }
         /*...*/
     }
 }
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
index 3d70e48..f4cafe1 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
@@ -44,7 +44,7 @@
 import androidx.compose.runtime.livedata.observeAsState
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
@@ -313,7 +313,7 @@
 private object StateSnippet13 {
     @Composable
     fun ExpandingCard(title: String, body: String) {
-        var expanded by savedInstanceState { false }
+        var expanded by rememberSaveable { mutableStateOf(false) }
         ExpandingCard(
             title = title,
             body = body,
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index 88bcb8c..c46ac2a 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -217,8 +217,8 @@
   }
 
   public final class ColorsKt {
-    method @androidx.compose.runtime.Composable public static long contentColorFor-8_81llA(long color);
-    method public static long contentColorFor-pz-ABmo(androidx.compose.material.Colors, long color);
+    method @androidx.compose.runtime.Composable public static long contentColorFor-8_81llA(long backgroundColor);
+    method public static long contentColorFor-pz-ABmo(androidx.compose.material.Colors, long backgroundColor);
     method public static androidx.compose.material.Colors darkColors-T8B3sC8(optional long primary, optional long primaryVariant, optional long secondary, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError);
     method public static androidx.compose.material.Colors lightColors-hW7iGhc(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError);
   }
@@ -377,12 +377,12 @@
   }
 
   public final class MaterialTheme {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public androidx.compose.material.Colors getColors();
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public androidx.compose.material.Shapes getShapes();
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public androidx.compose.material.Typography getTypography();
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final androidx.compose.material.Colors colors;
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final androidx.compose.material.Shapes shapes;
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final androidx.compose.material.Typography typography;
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Colors getColors();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Shapes getShapes();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Typography getTypography();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Colors colors;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Shapes shapes;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Typography typography;
     field public static final androidx.compose.material.MaterialTheme INSTANCE;
   }
 
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index 88bcb8c..c46ac2a 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -217,8 +217,8 @@
   }
 
   public final class ColorsKt {
-    method @androidx.compose.runtime.Composable public static long contentColorFor-8_81llA(long color);
-    method public static long contentColorFor-pz-ABmo(androidx.compose.material.Colors, long color);
+    method @androidx.compose.runtime.Composable public static long contentColorFor-8_81llA(long backgroundColor);
+    method public static long contentColorFor-pz-ABmo(androidx.compose.material.Colors, long backgroundColor);
     method public static androidx.compose.material.Colors darkColors-T8B3sC8(optional long primary, optional long primaryVariant, optional long secondary, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError);
     method public static androidx.compose.material.Colors lightColors-hW7iGhc(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError);
   }
@@ -377,12 +377,12 @@
   }
 
   public final class MaterialTheme {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public androidx.compose.material.Colors getColors();
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public androidx.compose.material.Shapes getShapes();
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public androidx.compose.material.Typography getTypography();
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final androidx.compose.material.Colors colors;
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final androidx.compose.material.Shapes shapes;
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final androidx.compose.material.Typography typography;
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Colors getColors();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Shapes getShapes();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Typography getTypography();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Colors colors;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Shapes shapes;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Typography typography;
     field public static final androidx.compose.material.MaterialTheme INSTANCE;
   }
 
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index 88bcb8c..c46ac2a 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -217,8 +217,8 @@
   }
 
   public final class ColorsKt {
-    method @androidx.compose.runtime.Composable public static long contentColorFor-8_81llA(long color);
-    method public static long contentColorFor-pz-ABmo(androidx.compose.material.Colors, long color);
+    method @androidx.compose.runtime.Composable public static long contentColorFor-8_81llA(long backgroundColor);
+    method public static long contentColorFor-pz-ABmo(androidx.compose.material.Colors, long backgroundColor);
     method public static androidx.compose.material.Colors darkColors-T8B3sC8(optional long primary, optional long primaryVariant, optional long secondary, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError);
     method public static androidx.compose.material.Colors lightColors-hW7iGhc(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError);
   }
@@ -377,12 +377,12 @@
   }
 
   public final class MaterialTheme {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public androidx.compose.material.Colors getColors();
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public androidx.compose.material.Shapes getShapes();
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public androidx.compose.material.Typography getTypography();
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final androidx.compose.material.Colors colors;
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final androidx.compose.material.Shapes shapes;
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final androidx.compose.material.Typography typography;
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Colors getColors();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Shapes getShapes();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Typography getTypography();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Colors colors;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Shapes shapes;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Typography typography;
     field public static final androidx.compose.material.MaterialTheme INSTANCE;
   }
 
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
index 9d74add..91d0050 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
@@ -44,7 +44,6 @@
 import androidx.compose.material.Text
 import androidx.compose.material.TopAppBar
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -267,7 +266,7 @@
         elevation = 4.dp,
         color = color,
         border = BorderStroke(2.dp, SolidColor(Color.Black.copy(alpha = 0.75f))),
-        content = emptyContent()
+        content = {}
     )
 }
 
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
index dc48864..275d7b4 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
@@ -57,7 +57,7 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -150,15 +150,15 @@
 @Composable
 fun MaterialTextFieldDemo() {
     Column(Modifier.verticalScroll(rememberScrollState()).padding(PaddingValues(10.dp))) {
-        var text by savedInstanceState { "" }
-        var leadingChecked by savedInstanceState { false }
-        var trailingChecked by savedInstanceState { false }
-        val characterCounterChecked by savedInstanceState { false }
-        var singleLineChecked by savedInstanceState { true }
-        var selectedOption by savedInstanceState { Option.None }
-        var selectedTextField by savedInstanceState { TextFieldType.Filled }
-        var disabled by savedInstanceState { false }
-        var readOnly by savedInstanceState { false }
+        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) {
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
index f631a97..07e7080 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
@@ -31,7 +31,8 @@
 import androidx.compose.material.icons.filled.Info
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.text.TextRange
@@ -44,7 +45,7 @@
 @Sampled
 @Composable
 fun SimpleTextFieldSample() {
-    var text by savedInstanceState { "" }
+    var text by rememberSaveable { mutableStateOf("") }
 
     TextField(
         value = text,
@@ -57,7 +58,7 @@
 @Sampled
 @Composable
 fun SimpleOutlinedTextFieldSample() {
-    var text by savedInstanceState { "" }
+    var text by rememberSaveable { mutableStateOf("") }
 
     OutlinedTextField(
         value = text,
@@ -69,7 +70,7 @@
 @Sampled
 @Composable
 fun TextFieldWithIcons() {
-    var text by savedInstanceState { "" }
+    var text by rememberSaveable { mutableStateOf("") }
 
     TextField(
         value = text,
@@ -83,7 +84,7 @@
 @Sampled
 @Composable
 fun TextFieldWithPlaceholder() {
-    var text by savedInstanceState { "" }
+    var text by rememberSaveable { mutableStateOf("") }
 
     TextField(
         value = text,
@@ -96,7 +97,7 @@
 @Sampled
 @Composable
 fun TextFieldWithErrorState() {
-    var text by savedInstanceState { "" }
+    var text by rememberSaveable { mutableStateOf("") }
     val isValid = text.count() > 5 && '@' in text
 
     TextField(
@@ -113,7 +114,7 @@
 @Sampled
 @Composable
 fun TextFieldWithHelperMessage() {
-    var text by savedInstanceState { "" }
+    var text by rememberSaveable { mutableStateOf("") }
     val invalidInput = text.count() < 5 || '@' !in text
 
     Column {
@@ -142,7 +143,7 @@
 @Sampled
 @Composable
 fun PasswordTextField() {
-    var password by savedInstanceState { "" }
+    var password by rememberSaveable { mutableStateOf("") }
     TextField(
         value = password,
         onValueChange = { password = it },
@@ -155,8 +156,8 @@
 @Sampled
 @Composable
 fun TextFieldSample() {
-    var text by savedInstanceState(saver = TextFieldValue.Saver) {
-        TextFieldValue("example", TextRange(0, 7))
+    var text by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+        mutableStateOf(TextFieldValue("example", TextRange(0, 7)))
     }
 
     TextField(
@@ -169,8 +170,8 @@
 @Sampled
 @Composable
 fun OutlinedTextFieldSample() {
-    var text by savedInstanceState(saver = TextFieldValue.Saver) {
-        TextFieldValue("example", TextRange(0, 7))
+    var text by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+        mutableStateOf(TextFieldValue("example", TextRange(0, 7)))
     }
 
     OutlinedTextField(
@@ -183,7 +184,7 @@
 @Sampled
 @Composable
 fun TextFieldWithHideKeyboardOnImeAction() {
-    var text by savedInstanceState { "" }
+    var text by rememberSaveable { mutableStateOf("") }
 
     TextField(
         value = text,
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
index e2cba15..2389196 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
@@ -22,7 +22,6 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Providers
-import androidx.compose.runtime.emptyContent
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.AmbientLayoutDirection
@@ -72,7 +71,7 @@
                 drawerContent = {
                     Box(Modifier.fillMaxSize().testTag("content"))
                 },
-                bodyContent = emptyContent()
+                bodyContent = {}
             )
         }
 
@@ -89,7 +88,7 @@
                 drawerContent = {
                     Box(Modifier.fillMaxSize().testTag("content"))
                 },
-                bodyContent = emptyContent()
+                bodyContent = {}
             )
         }
 
@@ -107,7 +106,7 @@
                 drawerContent = {
                     Box(Modifier.fillMaxSize().testTag("content"))
                 },
-                bodyContent = emptyContent()
+                bodyContent = {}
             )
         }
 
@@ -124,7 +123,7 @@
                 drawerContent = {
                     Box(Modifier.fillMaxSize().testTag("content"))
                 },
-                bodyContent = emptyContent()
+                bodyContent = {}
             )
         }
 
@@ -144,7 +143,7 @@
                 drawerContent = {
                     Box(Modifier.fillMaxSize().testTag("content"))
                 },
-                bodyContent = emptyContent()
+                bodyContent = {}
             )
         }
 
@@ -164,7 +163,7 @@
                 drawerContent = {
                     Box(Modifier.fillMaxSize().testTag("drawer"))
                 },
-                bodyContent = emptyContent()
+                bodyContent = {}
             )
         }
 
@@ -326,7 +325,7 @@
                 drawerContent = {
                     Box(Modifier.fillMaxSize().testTag("drawer"))
                 },
-                bodyContent = emptyContent()
+                bodyContent = {}
             )
         }
 
@@ -515,7 +514,7 @@
                 drawerContent = {
                     Box(Modifier.fillMaxSize().testTag("drawer"))
                 },
-                bodyContent = emptyContent()
+                bodyContent = {}
             )
         }
 
@@ -554,7 +553,7 @@
                 drawerContent = {
                     Box(Modifier.fillMaxSize().testTag("drawer"))
                 },
-                bodyContent = emptyContent()
+                bodyContent = {}
             )
         }
 
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
index f6e3fab..0858323 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
@@ -21,7 +21,6 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.preferredHeight
-import androidx.compose.runtime.emptyContent
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
@@ -66,7 +65,7 @@
         rule.setMaterialContent {
             ModalBottomSheetLayout(
                 sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden),
-                content = emptyContent(),
+                content = {},
                 sheetContent = {
                     Box(
                         Modifier
@@ -88,7 +87,7 @@
         rule.setMaterialContent {
             ModalBottomSheetLayout(
                 sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Expanded),
-                content = emptyContent(),
+                content = {},
                 sheetContent = {
                     Box(
                         Modifier
@@ -110,7 +109,7 @@
         rule.setMaterialContent {
             ModalBottomSheetLayout(
                 sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden),
-                content = emptyContent(),
+                content = {},
                 sheetContent = {
                     Box(
                         Modifier
@@ -131,7 +130,7 @@
         rule.setMaterialContent {
             ModalBottomSheetLayout(
                 sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Expanded),
-                content = emptyContent(),
+                content = {},
                 sheetContent = {
                     Box(
                         Modifier
@@ -151,7 +150,7 @@
         rule.setMaterialContent {
             ModalBottomSheetLayout(
                 sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.HalfExpanded),
-                content = emptyContent(),
+                content = {},
                 sheetContent = {
                     Box(
                         Modifier
@@ -173,7 +172,7 @@
         rule.setMaterialContent {
             ModalBottomSheetLayout(
                 sheetState = sheetState,
-                content = emptyContent(),
+                content = {},
                 sheetContent = {
                     Box(
                         Modifier
@@ -215,7 +214,7 @@
         rule.setMaterialContent {
             ModalBottomSheetLayout(
                 sheetState = sheetState,
-                content = emptyContent(),
+                content = {},
                 sheetContent = {
                     Box(
                         Modifier
@@ -256,7 +255,7 @@
         rule.setMaterialContent {
             ModalBottomSheetLayout(
                 sheetState = sheetState,
-                content = emptyContent(),
+                content = {},
                 sheetContent = {
                     Box(
                         Modifier
@@ -288,7 +287,7 @@
         rule.setMaterialContent {
             ModalBottomSheetLayout(
                 sheetState = sheetState,
-                content = emptyContent(),
+                content = {},
                 sheetContent = {
                     Box(
                         Modifier
@@ -319,7 +318,7 @@
         rule.setMaterialContent {
             ModalBottomSheetLayout(
                 sheetState = sheetState,
-                content = emptyContent(),
+                content = {},
                 sheetContent = {
                     Box(
                         Modifier
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
index 94b26c2..59074a7 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
@@ -22,7 +22,6 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredSize
-import androidx.compose.runtime.emptyContent
 import androidx.compose.testutils.assertPixels
 import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
@@ -115,7 +114,7 @@
                         Modifier.fillMaxSize().padding(2.dp),
                         elevation = 2.dp,
                         color = Color.Blue,
-                        content = emptyContent()
+                        content = {}
                     )
                 }
 
@@ -132,7 +131,7 @@
                             Modifier.fillMaxSize().padding(2.dp),
                             elevation = 2.dp,
                             color = Color.Blue,
-                            content = emptyContent()
+                            content = {}
                         )
                     }
                 }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Colors.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Colors.kt
index 2881b3c..ddf8d43 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Colors.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Colors.kt
@@ -227,19 +227,24 @@
 )
 
 /**
- * Tries to match [color] to a color in this Colors, and then returns the corresponding
- * `on` color.
+ * The Material color system contains pairs of colors that are typically used for the background
+ * and content color inside a component. For example, a [Button] typically uses `primary` for its
+ * background, and `onPrimary` for the color of its content (usually text or iconography).
  *
- * For example, when [color] is [Colors.primary], this will return
- * [Colors.onPrimary]. If [color] is not present in the theme, this will return `null`.
+ * This function tries to match the provided [backgroundColor] to a 'background' color in this
+ * [Colors], and then will return the corresponding color used for content. For example, when
+ * [backgroundColor] is [Colors.primary], this will return [Colors.onPrimary].
  *
- * @return the matching `on` color for [color]. If [color] is not part of the theme's
- * [Colors], then returns [Color.Unspecified].
+ * If [backgroundColor] does not match a background color in the theme, this will return
+ * [Color.Unspecified].
+ *
+ * @return the matching content color for [backgroundColor]. If [backgroundColor] is not present in
+ * the theme's [Colors], then returns [Color.Unspecified].
  *
  * @see contentColorFor
  */
-fun Colors.contentColorFor(color: Color): Color {
-    return when (color) {
+fun Colors.contentColorFor(backgroundColor: Color): Color {
+    return when (backgroundColor) {
         primary -> onPrimary
         primaryVariant -> onPrimary
         secondary -> onSecondary
@@ -252,15 +257,25 @@
 }
 
 /**
- * Tries to match [color] to a color in the current [Colors], and then returns the
- * corresponding `on` color. If [color] can not be matched to the palette, then this will return
- * the existing value for [AmbientContentColor] at this point in the tree.
+ * The Material color system contains pairs of colors that are typically used for the background
+ * and content color inside a component. For example, a [Button] typically uses `primary` for its
+ * background, and `onPrimary` for the color of its content (usually text or iconography).
+ *
+ * This function tries to match the provided [backgroundColor] to a 'background' color in this
+ * [Colors], and then will return the corresponding color used for content. For example, when
+ * [backgroundColor] is [Colors.primary], this will return [Colors.onPrimary].
+ *
+ * If [backgroundColor] does not match a background color in the theme, this will return
+ * the current value of [AmbientContentColor] as a best-effort color.
+ *
+ * @return the matching content color for [backgroundColor]. If [backgroundColor] is not present in
+ * the theme's [Colors], then returns the current value of [AmbientContentColor].
  *
  * @see Colors.contentColorFor
  */
 @Composable
-fun contentColorFor(color: Color) =
-    MaterialTheme.colors.contentColorFor(color).takeOrElse { AmbientContentColor.current }
+fun contentColorFor(backgroundColor: Color) =
+    MaterialTheme.colors.contentColorFor(backgroundColor).takeOrElse { AmbientContentColor.current }
 
 /**
  * Updates the internal values of the given [Colors] with values from the [other] [Colors]. This
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ElevationOverlay.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ElevationOverlay.kt
index 5b5d09f..665ce3b 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ElevationOverlay.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ElevationOverlay.kt
@@ -18,8 +18,8 @@
 
 import androidx.compose.runtime.Ambient
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposableContract
 import androidx.compose.runtime.ProvidableAmbient
+import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.staticAmbientOf
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.compositeOver
@@ -65,7 +65,7 @@
  * The default [ElevationOverlay] implementation.
  */
 private object DefaultElevationOverlay : ElevationOverlay {
-    @ComposableContract(readonly = true)
+    @ReadOnlyComposable
     @Composable
     override fun apply(color: Color, elevation: Dp): Color {
         val colors = MaterialTheme.colors
@@ -83,7 +83,7 @@
  * the resultant color. This color is the [contentColorFor] the [backgroundColor], with alpha
  * applied depending on the value of [elevation].
  */
-@ComposableContract(readonly = true)
+@ReadOnlyComposable
 @Composable
 private fun calculateForegroundColor(backgroundColor: Color, elevation: Dp): Color {
     val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt
index 8615164..a3517a6 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt
@@ -23,9 +23,9 @@
 import androidx.compose.material.ripple.RippleTheme
 import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposableContract
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Providers
+import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.selection.AmbientTextSelectionColors
 
@@ -96,7 +96,7 @@
      */
     val colors: Colors
         @Composable
-        @ComposableContract(readonly = true)
+        @ReadOnlyComposable
         get() = AmbientColors.current
 
     /**
@@ -106,7 +106,7 @@
      */
     val typography: Typography
         @Composable
-        @ComposableContract(readonly = true)
+        @ReadOnlyComposable
         get() = AmbientTypography.current
 
     /**
@@ -114,7 +114,7 @@
      */
     val shapes: Shapes
         @Composable
-        @ComposableContract(readonly = true)
+        @ReadOnlyComposable
         get() = AmbientShapes.current
 }
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
index fe7bc6d..5966f96 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
@@ -28,6 +28,7 @@
 import androidx.compose.animation.core.rememberInfiniteTransition
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.focusable
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.progressSemantics
 import androidx.compose.material.ProgressIndicatorDefaults.IndicatorBackgroundOpacity
@@ -75,6 +76,7 @@
         modifier
             .progressSemantics(progress)
             .preferredSize(LinearIndicatorWidth, LinearIndicatorHeight)
+            .focusable()
     ) {
         val strokeWidth = ProgressIndicatorDefaults.StrokeWidth.toPx()
         drawLinearIndicatorBackground(backgroundColor, strokeWidth)
@@ -148,6 +150,7 @@
         modifier
             .progressSemantics()
             .preferredSize(LinearIndicatorWidth, LinearIndicatorHeight)
+            .focusable()
     ) {
         val strokeWidth = ProgressIndicatorDefaults.StrokeWidth.toPx()
         drawLinearIndicatorBackground(backgroundColor, strokeWidth)
@@ -224,6 +227,7 @@
         modifier
             .progressSemantics(progress)
             .preferredSize(CircularIndicatorDiameter)
+            .focusable()
     ) {
         // Start at 12 O'clock
         val startAngle = 270f
@@ -301,6 +305,7 @@
         modifier
             .progressSemantics()
             .preferredSize(CircularIndicatorDiameter)
+            .focusable()
     ) {
 
         val currentRotationAngleOffset = (currentRotation * RotationAngleOffset) % 360f
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
index 8d9a5a9..3144396 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
@@ -22,7 +22,6 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.Stable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.staticAmbientOf
 import androidx.compose.ui.Modifier
@@ -148,10 +147,10 @@
 fun Scaffold(
     modifier: Modifier = Modifier,
     scaffoldState: ScaffoldState = rememberScaffoldState(),
-    topBar: @Composable () -> Unit = emptyContent(),
-    bottomBar: @Composable () -> Unit = emptyContent(),
+    topBar: @Composable () -> Unit = {},
+    bottomBar: @Composable () -> Unit = {},
     snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
-    floatingActionButton: @Composable () -> Unit = emptyContent(),
+    floatingActionButton: @Composable () -> Unit = {},
     floatingActionButtonPosition: FabPosition = FabPosition.End,
     isFloatingActionButtonDocked: Boolean = false,
     drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Surface.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Surface.kt
index 9e51afe..9e0b99e 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Surface.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Surface.kt
@@ -42,8 +42,8 @@
  *
  * 1) Clipping: Surface clips its children to the shape specified by [shape]
  *
- * 2) Elevation: Surface elevates its children on the Z axis by [elevation] pixels,
- * and draws the appropriate shadow.
+ * 2) Elevation: Surface draws a shadow to represent depth, where [elevation] represents the
+ * depth of this surface.
  *
  * 3) Borders: If [shape] has a border, then it will also be drawn.
  *
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
index 4b7e40a..79e179e 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
@@ -35,7 +35,6 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
@@ -180,7 +179,7 @@
                 indication = rememberRipple(bounded = false, radius = ThumbRippleRadius)
             )
             .size(ThumbDiameter),
-        content = emptyContent()
+        content = {}
     )
 }
 
diff --git a/compose/runtime/runtime-saveable/api/current.txt b/compose/runtime/runtime-saveable/api/current.txt
index 89f9634..f7e8664 100644
--- a/compose/runtime/runtime-saveable/api/current.txt
+++ b/compose/runtime/runtime-saveable/api/current.txt
@@ -11,6 +11,7 @@
 
   public final class RememberSaveableKt {
     method @androidx.compose.runtime.Composable public static <T> T rememberSaveable(Object![]? inputs, optional androidx.compose.runtime.saveable.Saver<T,?> saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> rememberSaveable(Object![]? inputs, androidx.compose.runtime.saveable.Saver<T,?> stateSaver, optional String? key, kotlin.jvm.functions.Function0<? extends androidx.compose.runtime.MutableState<T>> init);
   }
 
   public interface SaveableStateHolder {
@@ -22,6 +23,22 @@
     method @androidx.compose.runtime.Composable public static androidx.compose.runtime.saveable.SaveableStateHolder rememberSaveableStateHolder();
   }
 
+  public interface SaveableStateRegistry {
+    method public boolean canBeSaved(Object value);
+    method public Object? consumeRestored(String key);
+    method public java.util.Map<java.lang.String,java.util.List<java.lang.Object>> performSave();
+    method public androidx.compose.runtime.saveable.SaveableStateRegistry.Entry registerProvider(String key, kotlin.jvm.functions.Function0<?> valueProvider);
+  }
+
+  public static interface SaveableStateRegistry.Entry {
+    method public void unregister();
+  }
+
+  public final class SaveableStateRegistryKt {
+    method public static androidx.compose.runtime.saveable.SaveableStateRegistry SaveableStateRegistry(java.util.Map<java.lang.String,? extends java.util.List<?>>? restoredValues, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> canBeSaved);
+    method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.saveable.SaveableStateRegistry> getAmbientSaveableStateRegistry();
+  }
+
   public interface Saver<Original, Saveable> {
     method public Original? restore(Saveable value);
     method public Saveable? save(androidx.compose.runtime.saveable.SaverScope, Original? value);
@@ -42,11 +59,16 @@
 
   public final class DeprecatedKt {
     method @Deprecated public static <Original, Saveable> androidx.compose.runtime.savedinstancestate.Saver<Original,Saveable> Saver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.savedinstancestate.SaverScope,? super Original,? extends Saveable> save, kotlin.jvm.functions.Function1<? super Saveable,? extends Original> restore);
+    method @Deprecated public static androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry UiSavedStateRegistry(java.util.Map<java.lang.String,? extends java.util.List<?>>? restoredValues, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> canBeSaved);
     method @Deprecated public static <T> androidx.compose.runtime.savedinstancestate.Saver<T,java.lang.Object> autoSaver();
+    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry> getAmbientUiSavedStateRegistry();
     method @Deprecated public static <Original, Saveable> androidx.compose.runtime.savedinstancestate.Saver<Original,?> listSaver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.savedinstancestate.SaverScope,? super Original,? extends java.util.List<? extends Saveable>> save, kotlin.jvm.functions.Function1<? super java.util.List<? extends Saveable>,? extends Original> restore);
     method @Deprecated public static <T> androidx.compose.runtime.savedinstancestate.Saver<T,?> mapSaver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.savedinstancestate.SaverScope,? super T,? extends java.util.Map<java.lang.String,?>> save, kotlin.jvm.functions.Function1<? super java.util.Map<java.lang.String,?>,? extends T> restore);
     method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.savedinstancestate.RestorableStateHolder<T> rememberRestorableStateHolder();
     method @Deprecated @androidx.compose.runtime.Composable public static <T> T rememberSavedInstanceState(Object![]? inputs, optional androidx.compose.runtime.savedinstancestate.Saver<T,?>? saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(androidx.compose.runtime.savedinstancestate.Saver<T,?> saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(androidx.compose.runtime.saveable.Saver<T,?> saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
   }
 
   @Deprecated public @interface ExperimentalRestorableStateHolder {
@@ -55,28 +77,13 @@
   @Deprecated public interface RestorableStateHolder<T> extends androidx.compose.runtime.saveable.SaveableStateHolder {
   }
 
-  public final class SavedInstanceStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(Object![]? inputs, optional androidx.compose.runtime.saveable.Saver<T,?> saver, optional String? key, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> init);
-  }
-
   @Deprecated public interface Saver<Original, Saveable> extends androidx.compose.runtime.saveable.Saver<Original,Saveable> {
   }
 
   @Deprecated public interface SaverScope extends androidx.compose.runtime.saveable.SaverScope {
   }
 
-  public interface UiSavedStateRegistry {
-    method public boolean canBeSaved(Object value);
-    method public Object? consumeRestored(String key);
-    method public java.util.Map<java.lang.String,java.util.List<java.lang.Object>> performSave();
-    method public void registerProvider(String key, kotlin.jvm.functions.Function0<?> valueProvider);
-    method public void unregisterProvider(String key, kotlin.jvm.functions.Function0<?> valueProvider);
-  }
-
-  public final class UiSavedStateRegistryKt {
-    method public static androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry UiSavedStateRegistry(java.util.Map<java.lang.String,? extends java.util.List<?>>? restoredValues, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> canBeSaved);
-    method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry> getAmbientUiSavedStateRegistry();
-    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry>! getUiSavedStateRegistryAmbient();
+  @Deprecated public interface UiSavedStateRegistry extends androidx.compose.runtime.saveable.SaveableStateRegistry {
   }
 
 }
diff --git a/compose/runtime/runtime-saveable/api/public_plus_experimental_current.txt b/compose/runtime/runtime-saveable/api/public_plus_experimental_current.txt
index 89f9634..f7e8664 100644
--- a/compose/runtime/runtime-saveable/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime-saveable/api/public_plus_experimental_current.txt
@@ -11,6 +11,7 @@
 
   public final class RememberSaveableKt {
     method @androidx.compose.runtime.Composable public static <T> T rememberSaveable(Object![]? inputs, optional androidx.compose.runtime.saveable.Saver<T,?> saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> rememberSaveable(Object![]? inputs, androidx.compose.runtime.saveable.Saver<T,?> stateSaver, optional String? key, kotlin.jvm.functions.Function0<? extends androidx.compose.runtime.MutableState<T>> init);
   }
 
   public interface SaveableStateHolder {
@@ -22,6 +23,22 @@
     method @androidx.compose.runtime.Composable public static androidx.compose.runtime.saveable.SaveableStateHolder rememberSaveableStateHolder();
   }
 
+  public interface SaveableStateRegistry {
+    method public boolean canBeSaved(Object value);
+    method public Object? consumeRestored(String key);
+    method public java.util.Map<java.lang.String,java.util.List<java.lang.Object>> performSave();
+    method public androidx.compose.runtime.saveable.SaveableStateRegistry.Entry registerProvider(String key, kotlin.jvm.functions.Function0<?> valueProvider);
+  }
+
+  public static interface SaveableStateRegistry.Entry {
+    method public void unregister();
+  }
+
+  public final class SaveableStateRegistryKt {
+    method public static androidx.compose.runtime.saveable.SaveableStateRegistry SaveableStateRegistry(java.util.Map<java.lang.String,? extends java.util.List<?>>? restoredValues, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> canBeSaved);
+    method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.saveable.SaveableStateRegistry> getAmbientSaveableStateRegistry();
+  }
+
   public interface Saver<Original, Saveable> {
     method public Original? restore(Saveable value);
     method public Saveable? save(androidx.compose.runtime.saveable.SaverScope, Original? value);
@@ -42,11 +59,16 @@
 
   public final class DeprecatedKt {
     method @Deprecated public static <Original, Saveable> androidx.compose.runtime.savedinstancestate.Saver<Original,Saveable> Saver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.savedinstancestate.SaverScope,? super Original,? extends Saveable> save, kotlin.jvm.functions.Function1<? super Saveable,? extends Original> restore);
+    method @Deprecated public static androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry UiSavedStateRegistry(java.util.Map<java.lang.String,? extends java.util.List<?>>? restoredValues, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> canBeSaved);
     method @Deprecated public static <T> androidx.compose.runtime.savedinstancestate.Saver<T,java.lang.Object> autoSaver();
+    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry> getAmbientUiSavedStateRegistry();
     method @Deprecated public static <Original, Saveable> androidx.compose.runtime.savedinstancestate.Saver<Original,?> listSaver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.savedinstancestate.SaverScope,? super Original,? extends java.util.List<? extends Saveable>> save, kotlin.jvm.functions.Function1<? super java.util.List<? extends Saveable>,? extends Original> restore);
     method @Deprecated public static <T> androidx.compose.runtime.savedinstancestate.Saver<T,?> mapSaver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.savedinstancestate.SaverScope,? super T,? extends java.util.Map<java.lang.String,?>> save, kotlin.jvm.functions.Function1<? super java.util.Map<java.lang.String,?>,? extends T> restore);
     method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.savedinstancestate.RestorableStateHolder<T> rememberRestorableStateHolder();
     method @Deprecated @androidx.compose.runtime.Composable public static <T> T rememberSavedInstanceState(Object![]? inputs, optional androidx.compose.runtime.savedinstancestate.Saver<T,?>? saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(androidx.compose.runtime.savedinstancestate.Saver<T,?> saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(androidx.compose.runtime.saveable.Saver<T,?> saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
   }
 
   @Deprecated public @interface ExperimentalRestorableStateHolder {
@@ -55,28 +77,13 @@
   @Deprecated public interface RestorableStateHolder<T> extends androidx.compose.runtime.saveable.SaveableStateHolder {
   }
 
-  public final class SavedInstanceStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(Object![]? inputs, optional androidx.compose.runtime.saveable.Saver<T,?> saver, optional String? key, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> init);
-  }
-
   @Deprecated public interface Saver<Original, Saveable> extends androidx.compose.runtime.saveable.Saver<Original,Saveable> {
   }
 
   @Deprecated public interface SaverScope extends androidx.compose.runtime.saveable.SaverScope {
   }
 
-  public interface UiSavedStateRegistry {
-    method public boolean canBeSaved(Object value);
-    method public Object? consumeRestored(String key);
-    method public java.util.Map<java.lang.String,java.util.List<java.lang.Object>> performSave();
-    method public void registerProvider(String key, kotlin.jvm.functions.Function0<?> valueProvider);
-    method public void unregisterProvider(String key, kotlin.jvm.functions.Function0<?> valueProvider);
-  }
-
-  public final class UiSavedStateRegistryKt {
-    method public static androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry UiSavedStateRegistry(java.util.Map<java.lang.String,? extends java.util.List<?>>? restoredValues, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> canBeSaved);
-    method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry> getAmbientUiSavedStateRegistry();
-    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry>! getUiSavedStateRegistryAmbient();
+  @Deprecated public interface UiSavedStateRegistry extends androidx.compose.runtime.saveable.SaveableStateRegistry {
   }
 
 }
diff --git a/compose/runtime/runtime-saveable/api/restricted_current.txt b/compose/runtime/runtime-saveable/api/restricted_current.txt
index 89f9634..f7e8664 100644
--- a/compose/runtime/runtime-saveable/api/restricted_current.txt
+++ b/compose/runtime/runtime-saveable/api/restricted_current.txt
@@ -11,6 +11,7 @@
 
   public final class RememberSaveableKt {
     method @androidx.compose.runtime.Composable public static <T> T rememberSaveable(Object![]? inputs, optional androidx.compose.runtime.saveable.Saver<T,?> saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> rememberSaveable(Object![]? inputs, androidx.compose.runtime.saveable.Saver<T,?> stateSaver, optional String? key, kotlin.jvm.functions.Function0<? extends androidx.compose.runtime.MutableState<T>> init);
   }
 
   public interface SaveableStateHolder {
@@ -22,6 +23,22 @@
     method @androidx.compose.runtime.Composable public static androidx.compose.runtime.saveable.SaveableStateHolder rememberSaveableStateHolder();
   }
 
+  public interface SaveableStateRegistry {
+    method public boolean canBeSaved(Object value);
+    method public Object? consumeRestored(String key);
+    method public java.util.Map<java.lang.String,java.util.List<java.lang.Object>> performSave();
+    method public androidx.compose.runtime.saveable.SaveableStateRegistry.Entry registerProvider(String key, kotlin.jvm.functions.Function0<?> valueProvider);
+  }
+
+  public static interface SaveableStateRegistry.Entry {
+    method public void unregister();
+  }
+
+  public final class SaveableStateRegistryKt {
+    method public static androidx.compose.runtime.saveable.SaveableStateRegistry SaveableStateRegistry(java.util.Map<java.lang.String,? extends java.util.List<?>>? restoredValues, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> canBeSaved);
+    method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.saveable.SaveableStateRegistry> getAmbientSaveableStateRegistry();
+  }
+
   public interface Saver<Original, Saveable> {
     method public Original? restore(Saveable value);
     method public Saveable? save(androidx.compose.runtime.saveable.SaverScope, Original? value);
@@ -42,11 +59,16 @@
 
   public final class DeprecatedKt {
     method @Deprecated public static <Original, Saveable> androidx.compose.runtime.savedinstancestate.Saver<Original,Saveable> Saver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.savedinstancestate.SaverScope,? super Original,? extends Saveable> save, kotlin.jvm.functions.Function1<? super Saveable,? extends Original> restore);
+    method @Deprecated public static androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry UiSavedStateRegistry(java.util.Map<java.lang.String,? extends java.util.List<?>>? restoredValues, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> canBeSaved);
     method @Deprecated public static <T> androidx.compose.runtime.savedinstancestate.Saver<T,java.lang.Object> autoSaver();
+    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry> getAmbientUiSavedStateRegistry();
     method @Deprecated public static <Original, Saveable> androidx.compose.runtime.savedinstancestate.Saver<Original,?> listSaver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.savedinstancestate.SaverScope,? super Original,? extends java.util.List<? extends Saveable>> save, kotlin.jvm.functions.Function1<? super java.util.List<? extends Saveable>,? extends Original> restore);
     method @Deprecated public static <T> androidx.compose.runtime.savedinstancestate.Saver<T,?> mapSaver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.savedinstancestate.SaverScope,? super T,? extends java.util.Map<java.lang.String,?>> save, kotlin.jvm.functions.Function1<? super java.util.Map<java.lang.String,?>,? extends T> restore);
     method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.savedinstancestate.RestorableStateHolder<T> rememberRestorableStateHolder();
     method @Deprecated @androidx.compose.runtime.Composable public static <T> T rememberSavedInstanceState(Object![]? inputs, optional androidx.compose.runtime.savedinstancestate.Saver<T,?>? saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(androidx.compose.runtime.savedinstancestate.Saver<T,?> saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+    method @Deprecated @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(androidx.compose.runtime.saveable.Saver<T,?> saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
   }
 
   @Deprecated public @interface ExperimentalRestorableStateHolder {
@@ -55,28 +77,13 @@
   @Deprecated public interface RestorableStateHolder<T> extends androidx.compose.runtime.saveable.SaveableStateHolder {
   }
 
-  public final class SavedInstanceStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> savedInstanceState(Object![]? inputs, optional androidx.compose.runtime.saveable.Saver<T,?> saver, optional String? key, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> init);
-  }
-
   @Deprecated public interface Saver<Original, Saveable> extends androidx.compose.runtime.saveable.Saver<Original,Saveable> {
   }
 
   @Deprecated public interface SaverScope extends androidx.compose.runtime.saveable.SaverScope {
   }
 
-  public interface UiSavedStateRegistry {
-    method public boolean canBeSaved(Object value);
-    method public Object? consumeRestored(String key);
-    method public java.util.Map<java.lang.String,java.util.List<java.lang.Object>> performSave();
-    method public void registerProvider(String key, kotlin.jvm.functions.Function0<?> valueProvider);
-    method public void unregisterProvider(String key, kotlin.jvm.functions.Function0<?> valueProvider);
-  }
-
-  public final class UiSavedStateRegistryKt {
-    method public static androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry UiSavedStateRegistry(java.util.Map<java.lang.String,? extends java.util.List<?>>? restoredValues, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> canBeSaved);
-    method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry> getAmbientUiSavedStateRegistry();
-    method @Deprecated public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry>! getUiSavedStateRegistryAmbient();
+  @Deprecated public interface UiSavedStateRegistry extends androidx.compose.runtime.saveable.SaveableStateRegistry {
   }
 
 }
diff --git a/compose/runtime/runtime-saveable/samples/src/main/java/androidx/compose/runtime/saveable/samples/RememberSaveableSamples.kt b/compose/runtime/runtime-saveable/samples/src/main/java/androidx/compose/runtime/saveable/samples/RememberSaveableSamples.kt
index 0c983de..5b4a546 100644
--- a/compose/runtime/runtime-saveable/samples/src/main/java/androidx/compose/runtime/saveable/samples/RememberSaveableSamples.kt
+++ b/compose/runtime/runtime-saveable/samples/src/main/java/androidx/compose/runtime/saveable/samples/RememberSaveableSamples.kt
@@ -20,10 +20,39 @@
 
 import androidx.annotation.Sampled
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
 
 @Sampled
 @Composable
 fun RememberSaveable() {
     val list = rememberSaveable { mutableListOf<Int>() }
 }
+
+@Sampled
+@Composable
+fun RememberSaveableWithMutableState() {
+    var value by rememberSaveable { mutableStateOf(({ "value" })()) }
+}
+
+@Sampled
+@Composable
+fun RememberSaveableCustomSaver() {
+    val holder = rememberSaveable(saver = HolderSaver) { Holder(0) }
+}
+
+@Sampled
+@Composable
+fun RememberSaveableWithMutableStateAndCustomSaver() {
+    val holder = rememberSaveable(stateSaver = HolderSaver) { mutableStateOf(Holder(0)) }
+}
+
+private data class Holder(var value: Int)
+
+private val HolderSaver = Saver<Holder, Int>(
+    save = { it.value },
+    restore = { Holder(it) }
+)
\ No newline at end of file
diff --git a/compose/runtime/runtime-saveable/samples/src/main/java/androidx/compose/runtime/saveable/samples/SaveableStateHolderSamples.kt b/compose/runtime/runtime-saveable/samples/src/main/java/androidx/compose/runtime/saveable/samples/SaveableStateHolderSamples.kt
index 40ca1e1..8c5feb7 100644
--- a/compose/runtime/runtime-saveable/samples/src/main/java/androidx/compose/runtime/saveable/samples/SaveableStateHolderSamples.kt
+++ b/compose/runtime/runtime-saveable/samples/src/main/java/androidx/compose/runtime/saveable/samples/SaveableStateHolderSamples.kt
@@ -29,8 +29,9 @@
 import androidx.compose.material.Text
 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.saveable.rememberSaveableStateHolder
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -58,7 +59,7 @@
     }
 
     Column {
-        var screen by savedInstanceState { "screen1" }
+        var screen by rememberSaveable { mutableStateOf("screen1") }
         Row(horizontalArrangement = Arrangement.SpaceEvenly) {
             Button(onClick = { screen = "screen1" }) {
                 Text("Go to screen1")
@@ -79,7 +80,7 @@
 
 @Composable
 fun Screen1() {
-    var counter by savedInstanceState { 0 }
+    var counter by rememberSaveable { mutableStateOf(0) }
     Button(onClick = { counter++ }) {
         Text("Counter=$counter on Screen1")
     }
diff --git a/compose/runtime/runtime-saveable/samples/src/main/java/androidx/compose/runtime/savedinstancestate/samples/Samples.kt b/compose/runtime/runtime-saveable/samples/src/main/java/androidx/compose/runtime/savedinstancestate/samples/Samples.kt
deleted file mode 100644
index 060e9bd..0000000
--- a/compose/runtime/runtime-saveable/samples/src/main/java/androidx/compose/runtime/savedinstancestate/samples/Samples.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:Suppress("UNUSED_VARIABLE")
-
-package androidx.compose.runtime.savedinstancestate.samples
-
-import androidx.annotation.Sampled
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
-import androidx.compose.runtime.setValue
-
-@Sampled
-@Composable
-fun SavedInstanceStateSample() {
-    var value by savedInstanceState { "value" }
-}
diff --git a/compose/runtime/runtime-saveable/src/androidAndroidTest/AndroidManifest.xml b/compose/runtime/runtime-saveable/src/androidAndroidTest/AndroidManifest.xml
index 7aeb38a..e5e4475 100644
--- a/compose/runtime/runtime-saveable/src/androidAndroidTest/AndroidManifest.xml
+++ b/compose/runtime/runtime-saveable/src/androidAndroidTest/AndroidManifest.xml
@@ -22,9 +22,10 @@
         android:debuggable="false"
         tools:ignore="HardcodedDebugMode"
         tools:replace="android:debuggable">
-        <activity android:name="androidx.compose.runtime.savedinstancestate.RecreationTest1Activity" />
-        <activity android:name="androidx.compose.runtime.savedinstancestate.RecreationTest2Activity" />
-        <activity android:name="androidx.compose.runtime.savedinstancestate.RecreationTest3Activity" />
-        <activity android:name="androidx.compose.runtime.savedinstancestate.RecreationTest4Activity" />
+        <activity android:name="androidx.compose.runtime.saveable.RecreationTest1Activity" />
+        <activity android:name="androidx.compose.runtime.saveable.RecreationTest2Activity" />
+        <activity android:name="androidx.compose.runtime.saveable.RecreationTest3Activity" />
+        <activity android:name="androidx.compose.runtime.saveable.RecreationTest4Activity" />
+        <activity android:name="androidx.compose.runtime.saveable.RecreationTest5Activity" />
     </application>
 </manifest>
diff --git a/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/ActivityRecreationTest.kt b/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/ActivityRecreationTest.kt
index bf075ff..c8e912e 100644
--- a/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/ActivityRecreationTest.kt
+++ b/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/ActivityRecreationTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.compose.runtime.savedinstancestate
+package androidx.compose.runtime.saveable
 
 import android.os.Bundle
 import android.view.LayoutInflater
@@ -22,8 +22,9 @@
 import android.widget.FrameLayout
 import android.widget.LinearLayout
 import androidx.activity.ComponentActivity
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.saveable.test.R
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.setContent
@@ -131,6 +132,26 @@
         }
     }
 
+    @Test
+    fun valueStoredInMutableStateIsRestored() {
+        val activityScenario: ActivityScenario<RecreationTest5Activity> =
+            ActivityScenario.launch(RecreationTest5Activity::class.java)
+
+        activityScenario.moveToState(Lifecycle.State.RESUMED)
+
+        activityScenario.onActivity {
+            assertThat(it.state.value).isEqualTo(0)
+            // change the value, so we can assert this change will be restored
+            it.state.value = 1
+        }
+
+        activityScenario.recreate()
+
+        activityScenario.onActivity {
+            assertThat(it.state.value).isEqualTo(1)
+        }
+    }
+
     private fun FragmentActivity.findFragment(id: Int) =
         supportFragmentManager.findFragmentById(id) as TestFragment
 }
@@ -225,6 +246,18 @@
     }
 }
 
+class RecreationTest5Activity : ComponentActivity() {
+
+    lateinit var state: MutableState<Int>
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            state = rememberSaveable { mutableStateOf(0) }
+        }
+    }
+}
+
 class TestFragment : Fragment() {
 
     lateinit var array: IntArray
diff --git a/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RememberSaveableTest.kt b/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RememberSaveableTest.kt
index 9eef95a..25bcd69 100644
--- a/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RememberSaveableTest.kt
+++ b/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RememberSaveableTest.kt
@@ -20,8 +20,6 @@
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.savedinstancestate.AmbientUiSavedStateRegistry
-import androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.test.junit4.StateRestorationTester
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -122,9 +120,12 @@
             WrapRegistry(
                 wrap = {
                     object : DelegateRegistry(it) {
-                        override fun registerProvider(key: String, valueProvider: () -> Any?) {
+                        override fun registerProvider(
+                            key: String,
+                            valueProvider: () -> Any?
+                        ): SaveableStateRegistry.Entry {
                             provider = valueProvider
-                            super.registerProvider(key, valueProvider)
+                            return super.registerProvider(key, valueProvider)
                         }
                     }
                 }
@@ -147,9 +148,12 @@
             WrapRegistry(
                 wrap = {
                     object : DelegateRegistry(it) {
-                        override fun registerProvider(key: String, valueProvider: () -> Any?) {
+                        override fun registerProvider(
+                            key: String,
+                            valueProvider: () -> Any?
+                        ): SaveableStateRegistry.Entry {
                             provider = valueProvider
-                            super.registerProvider(key, valueProvider)
+                            return super.registerProvider(key, valueProvider)
                         }
                     }
                 }
@@ -166,12 +170,20 @@
     @Test
     fun unregistersFromPrevProviderAndRegistersToTheNewOne() {
         var unregisterCalledForKey: String? = null
-        var registryFactory by mutableStateOf<(UiSavedStateRegistry) -> UiSavedStateRegistry>(
+        var registryFactory by mutableStateOf<(SaveableStateRegistry) -> SaveableStateRegistry>(
             value = {
                 object : DelegateRegistry(it) {
-                    override fun unregisterProvider(key: String, valueProvider: () -> Any?) {
-                        unregisterCalledForKey = key
-                        super.unregisterProvider(key, valueProvider)
+                    override fun registerProvider(
+                        key: String,
+                        valueProvider: () -> Any?
+                    ): SaveableStateRegistry.Entry {
+                        val entry = super.registerProvider(key, valueProvider)
+                        return object : SaveableStateRegistry.Entry {
+                            override fun unregister() {
+                                unregisterCalledForKey = key
+                                entry.unregister()
+                            }
+                        }
                     }
                 }
             }
@@ -193,12 +205,16 @@
         rule.runOnUiThread {
             registryFactory = {
                 object : DelegateRegistry(it) {
-                    override fun registerProvider(key: String, valueProvider: () -> Any?) {
-                        super.registerProvider(key, valueProvider)
+                    override fun registerProvider(
+                        key: String,
+                        valueProvider: () -> Any?
+                    ): SaveableStateRegistry.Entry {
+                        val result = super.registerProvider(key, valueProvider)
                         // asserts that we unregistered from the previous registry and then
                         // registered with the same key
                         assertThat(key).isEqualTo(unregisterCalledForKey)
                         registerCalled = true
+                        return result
                     }
                 }
             }
@@ -217,15 +233,19 @@
             WrapRegistry(
                 wrap = {
                     object : DelegateRegistry(it) {
-                        override fun registerProvider(key: String, valueProvider: () -> Any?) {
-                            super.registerProvider(key, valueProvider)
+                        override fun registerProvider(
+                            key: String,
+                            valueProvider: () -> Any?
+                        ): SaveableStateRegistry.Entry {
+                            val entry = super.registerProvider(key, valueProvider)
                             registeredKeys.add(key)
                             registerCalled++
-                        }
-
-                        override fun unregisterProvider(key: String, valueProvider: () -> Any?) {
-                            super.unregisterProvider(key, valueProvider)
-                            registeredKeys.remove(key)
+                            return object : SaveableStateRegistry.Entry {
+                                override fun unregister() {
+                                    registeredKeys.remove(key)
+                                    entry.unregister()
+                                }
+                            }
                         }
                     }
                 }
@@ -282,9 +302,17 @@
             WrapRegistry(
                 wrap = {
                     object : DelegateRegistry(it) {
-                        override fun unregisterProvider(key: String, valueProvider: () -> Any?) {
-                            onUnregisterCalled = true
-                            super.unregisterProvider(key, valueProvider)
+                        override fun registerProvider(
+                            key: String,
+                            valueProvider: () -> Any?
+                        ): SaveableStateRegistry.Entry {
+                            val entry = super.registerProvider(key, valueProvider)
+                            return object : SaveableStateRegistry.Entry {
+                                override fun unregister() {
+                                    onUnregisterCalled = true
+                                    entry.unregister()
+                                }
+                            }
                         }
                     }
                 }
@@ -312,9 +340,12 @@
             WrapRegistry(
                 wrap = {
                     object : DelegateRegistry(it) {
-                        override fun registerProvider(key: String, valueProvider: () -> Any?) {
+                        override fun registerProvider(
+                            key: String,
+                            valueProvider: () -> Any?
+                        ): SaveableStateRegistry.Entry {
                             actualKey = key
-                            super.registerProvider(key, valueProvider)
+                            return super.registerProvider(key, valueProvider)
                         }
                     }
                 }
@@ -334,9 +365,12 @@
             WrapRegistry(
                 wrap = {
                     object : DelegateRegistry(it) {
-                        override fun registerProvider(key: String, valueProvider: () -> Any?) {
+                        override fun registerProvider(
+                            key: String,
+                            valueProvider: () -> Any?
+                        ): SaveableStateRegistry.Entry {
                             actualKey = key
-                            super.registerProvider(key, valueProvider)
+                            return super.registerProvider(key, valueProvider)
                         }
                     }
                 }
@@ -352,14 +386,14 @@
 
 @Composable
 private fun WrapRegistry(
-    wrap: @Composable (UiSavedStateRegistry) -> UiSavedStateRegistry,
+    wrap: @Composable (SaveableStateRegistry) -> SaveableStateRegistry,
     content: @Composable () -> Unit
 ) {
     Providers(
-        AmbientUiSavedStateRegistry provides wrap(AmbientUiSavedStateRegistry.current!!),
+        AmbientSaveableStateRegistry provides wrap(AmbientSaveableStateRegistry.current!!),
         content = content
     )
 }
 
-private open class DelegateRegistry(original: UiSavedStateRegistry) :
-    UiSavedStateRegistry by original
\ No newline at end of file
+private open class DelegateRegistry(original: SaveableStateRegistry) :
+    SaveableStateRegistry by original
\ No newline at end of file
diff --git a/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceStateTest.kt b/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RememberSaveableWithMutableStateTest.kt
similarity index 86%
rename from compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceStateTest.kt
rename to compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RememberSaveableWithMutableStateTest.kt
index 75426ad..94d96e7 100644
--- a/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceStateTest.kt
+++ b/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RememberSaveableWithMutableStateTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.compose.runtime.savedinstancestate
+package androidx.compose.runtime.saveable
 
 import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.saveable.Holder
-import androidx.compose.runtime.saveable.HolderSaver
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.test.junit4.StateRestorationTester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -30,7 +29,7 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-class SavedInstanceStateTest {
+class RememberSaveableWithMutableStateTest {
 
     @get:Rule
     val rule = createComposeRule()
@@ -41,7 +40,7 @@
     fun simpleRestore() {
         var state: MutableState<Int>? = null
         restorationTester.setContent {
-            state = savedInstanceState { 0 }
+            state = rememberSaveable { mutableStateOf(0) }
         }
 
         rule.runOnUiThread {
@@ -63,8 +62,8 @@
     fun restoreWithSaver() {
         var state: MutableState<Holder>? = null
         restorationTester.setContent {
-            state = savedInstanceState(saver = HolderSaver) {
-                Holder(0)
+            state = rememberSaveable(stateSaver = HolderSaver) {
+                mutableStateOf(Holder(0))
             }
         }
 
@@ -87,7 +86,7 @@
     fun nullableStateRestoresNonNullValue() {
         var state: MutableState<String?>? = null
         restorationTester.setContent {
-            state = savedInstanceState<String?> { null }
+            state = rememberSaveable { mutableStateOf(null) }
         }
 
         rule.runOnUiThread {
@@ -109,7 +108,7 @@
     fun nullableStateRestoresNullValue() {
         var state: MutableState<String?>? = null
         restorationTester.setContent {
-            state = savedInstanceState<String?> { "initial" }
+            state = rememberSaveable { mutableStateOf("initial") }
         }
 
         rule.runOnUiThread {
diff --git a/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/RestorationInVariousScenariosTest.kt b/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RestorationInVariousScenariosTest.kt
similarity index 90%
rename from compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/RestorationInVariousScenariosTest.kt
rename to compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RestorationInVariousScenariosTest.kt
index 9722ab5..20b47f6 100644
--- a/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/RestorationInVariousScenariosTest.kt
+++ b/compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/RestorationInVariousScenariosTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package androidx.compose.runtime.savedinstancestate
+package androidx.compose.runtime.saveable
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.test.junit4.StateRestorationTester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -42,7 +43,7 @@
         val states = arrayOfNulls<MutableState<Int>>(2)
         restorationTester.setContent {
             for (i in 0..1) {
-                states[i] = savedInstanceState { 0 }
+                states[i] = rememberSaveable { mutableStateOf(0) }
             }
         }
 
@@ -72,7 +73,7 @@
         restorationTester.setContent {
             for (i in 0..1) {
                 key(i) {
-                    states[i] = savedInstanceState { 0 }
+                    states[i] = rememberSaveable { mutableStateOf(0) }
                 }
             }
         }
@@ -133,9 +134,9 @@
         var stateOutside: MutableState<String>? = null
         restorationTester.setContent {
             repeat(number) {
-                statesInLoop[it] = savedInstanceState { 0 }
+                statesInLoop[it] = rememberSaveable { mutableStateOf(0) }
             }
-            stateOutside = savedInstanceState { "0" }
+            stateOutside = rememberSaveable { mutableStateOf("0") }
         }
 
         rule.runOnIdle {
@@ -158,8 +159,8 @@
     fun twoStates() {
         val states = arrayOfNulls<MutableState<Int>>(2)
         restorationTester.setContent {
-            states[0] = savedInstanceState { 0 }
-            states[1] = savedInstanceState { 0 }
+            states[0] = rememberSaveable { mutableStateOf(0) }
+            states[1] = rememberSaveable { mutableStateOf(0) }
         }
 
         rule.runOnUiThread {
@@ -188,9 +189,9 @@
         val states = arrayOfNulls<MutableState<Int>>(2)
         restorationTester.setContent {
             if (needFirst) {
-                states[0] = savedInstanceState { 0 }
+                states[0] = rememberSaveable { mutableStateOf(0) }
             }
-            states[1] = savedInstanceState { 0 }
+            states[1] = rememberSaveable { mutableStateOf(0) }
         }
 
         rule.runOnUiThread {
@@ -276,6 +277,6 @@
 
     @Composable
     fun FunctionWithState(states: Array<MutableState<Int>?>, index: Int) {
-        states[index] = savedInstanceState { 0 }
+        states[index] = rememberSaveable { mutableStateOf(0) }
     }
 }
diff --git a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/ListSaver.kt b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/ListSaver.kt
index d3b7388..c4fd30e 100644
--- a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/ListSaver.kt
+++ b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/ListSaver.kt
@@ -20,10 +20,10 @@
  * The [Saver] implementation which allows to represent your [Original] class as a list of
  * [Saveable] values.
  *
- * What types can be saved is defined by [UiSavedStateRegistry], by default everything which can
+ * What types can be saved is defined by [SaveableStateRegistry], by default everything which can
  * be stored in the Bundle class can be saved.
  *
- * You can use it as a parameter for [savedInstanceState] or [rememberSaveable].
+ * You can use it as a parameter for [rememberSaveable].
  *
  * @sample androidx.compose.runtime.saveable.samples.ListSaverSample
  */
diff --git a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/MapSaver.kt b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/MapSaver.kt
index acc3763..6ad0912 100644
--- a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/MapSaver.kt
+++ b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/MapSaver.kt
@@ -20,10 +20,10 @@
  * The [Saver] implementation which allows to represent your class as a map of values which can
  * be saved individually.
  *
- * What types can be saved is defined by [UiSavedStateRegistry], by default everything which can
+ * What types can be saved is defined by [SaveableStateRegistry], by default everything which can
  * be stored in the Bundle class can be saved.
  *
- * You can use it as a parameter for [savedInstanceState] or [rememberSaveable].
+ * You can use it as a parameter for [rememberSaveable].
  *
  * @sample androidx.compose.runtime.saveable.samples.MapSaverSample
  */
diff --git a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt
index de83acc..2f53714 100644
--- a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt
+++ b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt
@@ -19,11 +19,14 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.SnapshotMutationPolicy
 import androidx.compose.runtime.currentCompositeKeyHash
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.neverEqualPolicy
+import androidx.compose.runtime.referentialEqualityPolicy
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.AmbientUiSavedStateRegistry
-import androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry
+import androidx.compose.runtime.snapshots.SnapshotMutableState
+import androidx.compose.runtime.structuralEqualityPolicy
 
 /**
  * Remember the value produced by [init].
@@ -34,15 +37,21 @@
  *
  * @sample androidx.compose.runtime.saveable.samples.RememberSaveable
  *
- * This function works nicely with mutable objects, when you update the state of this object
- * instead of recreating it. If you work with immutable objects [savedInstanceState] can suit you
- * more as it wraps the value into the [MutableState].
- *
  * If you use it with types which can be stored inside the Bundle then it will be saved and
  * restored automatically using [autoSaver], otherwise you will need to provide a custom [Saver]
  * implementation via the [saver] param.
  *
- * @sample androidx.compose.runtime.saveable.samples.CustomSaverSample
+ * @sample androidx.compose.runtime.saveable.samples.RememberSaveableCustomSaver
+ *
+ * You can use it with a value stored inside [androidx.compose.runtime.mutableStateOf].
+ *
+ * @sample androidx.compose.runtime.saveable.samples.RememberSaveableWithMutableState
+ *
+ * If the value inside the MutableState can be stored inside the Bundle it would be saved
+ * and restored automatically, otherwise you will need to provide a custom [Saver]
+ * implementation via an overload with which has `stateSaver` param.
+ *
+ * @sample androidx.compose.runtime.saveable.samples.RememberSaveableWithMutableStateAndCustomSaver
  *
  * @param inputs A set of inputs such that, when any of them have changed, will cause the state to
  * reset and [init] to be rerun
@@ -68,7 +77,7 @@
     @Suppress("UNCHECKED_CAST")
     (saver as Saver<T, Any>)
 
-    val registry = AmbientUiSavedStateRegistry.current
+    val registry = AmbientSaveableStateRegistry.current
     // value is restored using the registry or created via [init] lambda
     val value = remember(*inputs) {
         // TODO not restore when the input values changed (use hashKeys?) b/152014032
@@ -91,25 +100,91 @@
                 with(saverHolder.value) { SaverScope { registry.canBeSaved(it) }.save(value) }
             }
             registry.requireCanBeSaved(valueProvider())
-            registry.registerProvider(finalKey, valueProvider)
+            val entry = registry.registerProvider(finalKey, valueProvider)
             onDispose {
-                registry.unregisterProvider(finalKey, valueProvider)
+                entry.unregister()
             }
         }
     }
     return value
 }
 
-private fun UiSavedStateRegistry.requireCanBeSaved(value: Any?) {
+/**
+ * Remember the value produced by [init].
+ *
+ * It behaves similarly to [remember], but the stored value will survive the activity or process
+ * recreation using the saved instance state mechanism (for example it happens when the screen is
+ * rotated in the Android application).
+ *
+ * Use this overload if you remember a mutable state with a type which can't be stored in the
+ * Bundle so you have to provide a custom saver object.
+ *
+ * @sample androidx.compose.runtime.saveable.samples.RememberSaveableWithMutableStateAndCustomSaver
+ *
+ * @param inputs A set of inputs such that, when any of them have changed, will cause the state to
+ * reset and [init] to be rerun
+ * @param stateSaver The [Saver] object which defines how the value inside the MutableState is
+ * saved and restored.
+ * @param key An optional key to be used as a key for the saved value. If not provided we use the
+ * automatically generated by the Compose runtime which is unique for the every exact code location
+ * in the composition tree
+ * @param init A factory function to create the initial value of this state
+ */
+@Composable
+fun <T> rememberSaveable(
+    vararg inputs: Any?,
+    stateSaver: Saver<T, out Any>,
+    key: String? = null,
+    init: () -> MutableState<T>
+): MutableState<T> = rememberSaveable(
+    inputs,
+    saver = mutableStateSaver(stateSaver),
+    key = key,
+    init = init
+)
+
+@Suppress("UNCHECKED_CAST")
+private fun <T> mutableStateSaver(inner: Saver<T, out Any>) = with(inner as Saver<T, Any>) {
+    Saver<MutableState<T>, MutableState<Any?>>(
+        save = { state ->
+            require(state is SnapshotMutableState<T>) {
+                "If you use a custom MutableState implementation you have to write a custom " +
+                    "Saver and pass it as a saver param to rememberSaveable()"
+            }
+            mutableStateOf(save(state.value), state.policy as SnapshotMutationPolicy<Any?>)
+        },
+        restore = @Suppress("UNCHECKED_CAST") {
+            require(it is SnapshotMutableState<Any?>)
+            mutableStateOf(
+                if (it.value != null) restore(it.value!!) else null,
+                it.policy as SnapshotMutationPolicy<T?>
+            ) as MutableState<T>
+        }
+    )
+}
+
+private fun SaveableStateRegistry.requireCanBeSaved(value: Any?) {
     if (value != null && !canBeSaved(value)) {
         throw IllegalArgumentException(
-            if (value is MutableState<*>) {
-                "Please use savedInstanceState() if you want to save a MutableState"
+            if (value is SnapshotMutableState<*>) {
+                if (value.policy !== neverEqualPolicy<Any?>() &&
+                    value.policy !== structuralEqualityPolicy<Any?>() &&
+                    value.policy !== referentialEqualityPolicy<Any?>()
+                ) {
+                    "If you use a custom SnapshotMutationPolicy for your MutableState you have to" +
+                        " write a custom Saver"
+                } else {
+                    "MutableState containing ${value.value} cannot be saved using the current " +
+                        "SaveableStateRegistry. The default implementation only supports types " +
+                        "which can be stored inside the Bundle. Please consider implementing a " +
+                        "custom Saver for this class and pass it as a stateSaver parameter to " +
+                        "rememberSaveable()."
+                }
             } else {
-                "$value cannot be saved using the current UiSavedStateRegistry. The default " +
+                "$value cannot be saved using the current SaveableStateRegistry. The default " +
                     "implementation only supports types which can be stored inside the Bundle" +
                     ". Please consider implementing a custom Saver for this class and pass it" +
-                    " to savedInstanceState() or rememberSaveable()."
+                    " to rememberSaveable()."
             }
         )
     }
diff --git a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/SaveableStateHolder.kt b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/SaveableStateHolder.kt
index ef4a926..3c24c63 100644
--- a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/SaveableStateHolder.kt
+++ b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/SaveableStateHolder.kt
@@ -22,14 +22,12 @@
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.key
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.AmbientUiSavedStateRegistry
-import androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry
 
 /**
- * Allows to save the state defined with [savedInstanceState] and [rememberSaveable]
- * for the subtree before disposing it to make it possible to compose it back next time with the
- * restored state. It allows different navigation patterns to keep the ui state like scroll
- * position for the currently not composed screens from the backstack.
+ * Allows to save the state defined with [rememberSaveable] for the subtree before disposing it
+ * to make it possible to compose it back next time with the restored state. It allows different
+ * navigation patterns to keep the ui state like scroll position for the currently not composed
+ * screens from the backstack.
  *
  * @sample androidx.compose.runtime.saveable.samples.SimpleNavigationWithSaveableStateSample
  *
@@ -40,9 +38,8 @@
 interface SaveableStateHolder {
     /**
      * Put your content associated with a [key] inside the [content]. This will automatically
-     * save all the states defined with [savedInstanceState] and [rememberSaveable]
-     * before disposing the content and will restore the states when you compose with this key
-     * again.
+     * save all the states defined with [rememberSaveable] before disposing the content and will
+     * restore the states when you compose with this key again.
      *
      * @param key to be used for saving and restoring the states for the subtree. Note that on
      * Android you can only use types which can be stored inside the Bundle.
@@ -66,28 +63,28 @@
     ) {
         SaveableStateHolderImpl()
     }.apply {
-        parentSavedStateRegistry = AmbientUiSavedStateRegistry.current
+        parentSaveableStateRegistry = AmbientSaveableStateRegistry.current
     }
 
 private class SaveableStateHolderImpl(
     private val savedStates: MutableMap<Any, Map<String, List<Any?>>> = mutableMapOf()
 ) : SaveableStateHolder {
     private val registryHolders = mutableMapOf<Any, RegistryHolder>()
-    var parentSavedStateRegistry: UiSavedStateRegistry? = null
+    var parentSaveableStateRegistry: SaveableStateRegistry? = null
 
     @OptIn(ExperimentalComposeApi::class)
     @Composable
     override fun SaveableStateProvider(key: Any, content: @Composable () -> Unit) {
         key(key) {
             val registryHolder = remember {
-                require(parentSavedStateRegistry?.canBeSaved(key) ?: true) {
+                require(parentSaveableStateRegistry?.canBeSaved(key) ?: true) {
                     "Type of the key used for withRestorableState is not supported. On Android " +
                         "you can only use types which can be stored inside the Bundle."
                 }
                 RegistryHolder(key)
             }
             Providers(
-                AmbientUiSavedStateRegistry provides registryHolder.registry,
+                AmbientSaveableStateRegistry provides registryHolder.registry,
                 content = content
             )
             DisposableEffect(key) {
@@ -121,8 +118,8 @@
         val key: Any
     ) {
         var shouldSave = true
-        val registry: UiSavedStateRegistry = UiSavedStateRegistry(savedStates[key]) {
-            parentSavedStateRegistry?.canBeSaved(it) ?: true
+        val registry: SaveableStateRegistry = SaveableStateRegistry(savedStates[key]) {
+            parentSaveableStateRegistry?.canBeSaved(it) ?: true
         }
 
         fun saveTo(map: MutableMap<Any, Map<String, List<Any?>>>) {
diff --git a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistry.kt b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/SaveableStateRegistry.kt
similarity index 74%
rename from compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistry.kt
rename to compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/SaveableStateRegistry.kt
index b27176a..4821841 100644
--- a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistry.kt
+++ b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/SaveableStateRegistry.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-package androidx.compose.runtime.savedinstancestate
+package androidx.compose.runtime.saveable
 
+import androidx.compose.runtime.saveable.SaveableStateRegistry.Entry
 import androidx.compose.runtime.staticAmbientOf
 
 /**
  * Allows components to save and restore their state using the saved instance state mechanism.
  */
-interface UiSavedStateRegistry {
+interface SaveableStateRegistry {
     /**
      * Returns the restored value for the given key.
      * Once being restored the value is cleared, so you can't restore the same key twice.
@@ -44,16 +45,9 @@
      * @param key Key to use for storing the value
      * @param valueProvider Provides the current value, to be executed when [performSave]
      * will be triggered to collect all the registered values
+     * @return the registry entry which you can use to unregister the provider
      */
-    fun registerProvider(key: String, valueProvider: () -> Any?)
-
-    /**
-     * Unregisters the value provider previously registered via [registerProvider].
-     *
-     * @param key Key of the value which shouldn't be saved anymore
-     * @param valueProvider The provider previously passed to [registerProvider]
-     */
-    fun unregisterProvider(key: String, valueProvider: () -> Any?)
+    fun registerProvider(key: String, valueProvider: () -> Any?): Entry
 
     /**
      * Returns true if the value can be saved using this Registry.
@@ -68,41 +62,38 @@
      * a list of values for each key as it is allowed to have multiple providers for the same key.
      */
     fun performSave(): Map<String, List<Any?>>
+
+    /**
+     * The registry entry which you get when you use [registerProvider].
+     */
+    interface Entry {
+        /**
+         * Unregister previously registered entry.
+         */
+        fun unregister()
+    }
 }
 
 /**
- * Creates [UiSavedStateRegistry].
+ * Creates [SaveableStateRegistry].
  *
  * @param restoredValues The map of the restored values
  * @param canBeSaved Function which returns true if the given value can be saved by the registry
  */
-fun UiSavedStateRegistry(
+fun SaveableStateRegistry(
     restoredValues: Map<String, List<Any?>>?,
     canBeSaved: (Any) -> Boolean
-): UiSavedStateRegistry = UiSavedStateRegistryImpl(restoredValues, canBeSaved)
+): SaveableStateRegistry = SaveableStateRegistryImpl(restoredValues, canBeSaved)
 
 /**
- * Ambient with a current [UiSavedStateRegistry] instance.
+ * Ambient with a current [SaveableStateRegistry] instance.
  */
-@Suppress("AmbientNaming")
-@Deprecated(
-    "Renamed to AmbientUiSavedStateRegistry",
-    replaceWith = ReplaceWith(
-        "AmbientUiSavedStateRegistry",
-        "androidx.compose.runtime.savedinstancestate.AmbientUiSavedStateRegistry"
-    )
-)
-val UiSavedStateRegistryAmbient get() = AmbientUiSavedStateRegistry
+val AmbientSaveableStateRegistry = staticAmbientOf<SaveableStateRegistry?> { null }
 
-/**
- * Ambient with a current [UiSavedStateRegistry] instance.
- */
-val AmbientUiSavedStateRegistry = staticAmbientOf<UiSavedStateRegistry?> { null }
-
-private class UiSavedStateRegistryImpl(
+private class SaveableStateRegistryImpl(
     restored: Map<String, List<Any?>>?,
     private val canBeSaved: (Any) -> Boolean
-) : UiSavedStateRegistry {
+) : SaveableStateRegistry {
 
     private val restored: MutableMap<String, List<Any?>> =
         restored?.toMutableMap() ?: mutableMapOf()
@@ -122,21 +113,19 @@
         }
     }
 
-    override fun registerProvider(key: String, valueProvider: () -> Any?) {
+    override fun registerProvider(key: String, valueProvider: () -> Any?): Entry {
         require(key.isNotBlank()) { "Registered key is empty or blank" }
         @Suppress("UNCHECKED_CAST")
         valueProviders.getOrPut(key) { mutableListOf() }.add(valueProvider)
-    }
-
-    override fun unregisterProvider(key: String, valueProvider: () -> Any?) {
-        val list = valueProviders.remove(key)
-        val found = list?.remove(valueProvider)
-        require(found == true) {
-            "The given key $key , valueProvider pair wasn't previously registered"
-        }
-        if (list.isNotEmpty()) {
-            // if there are other providers for this key return list back to the map
-            valueProviders[key] = list
+        return object : Entry {
+            override fun unregister() {
+                val list = valueProviders.remove(key)
+                list?.remove(valueProvider)
+                if (list != null && list.isNotEmpty()) {
+                    // if there are other providers for this key return list back to the map
+                    valueProviders[key] = list
+                }
+            }
         }
     }
 
diff --git a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/Saver.kt b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/Saver.kt
index 3f43303..9d98db9 100644
--- a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/Saver.kt
+++ b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/Saver.kt
@@ -20,12 +20,11 @@
  * The [Saver] describes how the object of [Original] class can be simplified and converted into
  * something which is [Saveable].
  *
- * What types can be saved is defined by [UiSavedStateRegistry], by default everything which can
+ * What types can be saved is defined by [SaveableStateRegistry], by default everything which can
  * be stored in the Bundle class can be saved.
  * The implementations can check that the provided value can be saved via [SaverScope.canBeSaved]
  *
- * You can pass the implementations of this class as a parameter for [savedInstanceState] or
- * [rememberSaveable].
+ * You can pass the implementations of this class as a parameter for [rememberSaveable].
  *
  * @sample androidx.compose.runtime.saveable.samples.CustomSaverSample
  */
@@ -46,12 +45,11 @@
  * The [Saver] describes how the object of [Original] class can be simplified and converted into
  * something which is [Saveable].
  *
- * What types can be saved is defined by [UiSavedStateRegistry], by default everything which can
+ * What types can be saved is defined by [SaveableStateRegistry], by default everything which can
  * be stored in the Bundle class can be saved.
  * The implementations can check that the provided value can be saved via [SaverScope.canBeSaved]
  *
- * You can pass the implementations of this class as a parameter for [savedInstanceState] or
- * [rememberSaveable].
+ * You can pass the implementations of this class as a parameter for [rememberSaveable].
  *
  * @sample androidx.compose.runtime.saveable.samples.CustomSaverSample
  *
@@ -78,7 +76,7 @@
  */
 fun interface SaverScope {
     /**
-     * What types can be saved is defined by [UiSavedStateRegistry], by default everything which can
+     * What types can be saved is defined by [SaveableStateRegistry], by default everything which can
      * be stored in the Bundle class can be saved.
      */
     fun canBeSaved(value: Any): Boolean
@@ -87,7 +85,7 @@
 /**
  * The default implementation of [Saver] which does not perform any conversion.
  *
- * It is used by [savedInstanceState] and [rememberSaveable] by default.
+ * It is used by [rememberSaveable] by default.
  *
  * @see Saver
  */
diff --git a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/Deprecated.kt b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/Deprecated.kt
index 55f58b9..43e210f 100644
--- a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/Deprecated.kt
+++ b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/Deprecated.kt
@@ -21,6 +21,9 @@
 package androidx.compose.runtime.savedinstancestate
 
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.saveable.SaveableStateRegistry
+import androidx.compose.runtime.staticAmbientOf
 
 @Deprecated(
     "It was moved to androidx.compose.runtime.saveable package",
@@ -127,3 +130,108 @@
 ): T = throw IllegalStateException(
     "This method is deprecated and moved to androidx.compose.runtime.saveable package"
 )
+
+@Deprecated(
+    "It was renamed to SaveableStateRegistry and moved to androidx.compose.runtime.saveable" +
+        " package",
+    ReplaceWith(
+        "SaveableStateRegistry",
+        "androidx.compose.runtime.saveable.SaveableStateRegistry"
+    ),
+    level = DeprecationLevel.ERROR
+)
+interface UiSavedStateRegistry : SaveableStateRegistry
+
+@Deprecated(
+    "It was renamed to SaveableStateRegistry and moved to androidx.compose.runtime.saveable" +
+        " package",
+    ReplaceWith(
+        "SaveableStateRegistry(restoredValues, canBeSaved)",
+        "androidx.compose.runtime.saveable.SaveableStateRegistry"
+    ),
+    level = DeprecationLevel.ERROR
+)
+@Suppress("DocumentExceptions")
+fun UiSavedStateRegistry(
+    restoredValues: Map<String, List<Any?>>?,
+    canBeSaved: (Any) -> Boolean
+): UiSavedStateRegistry = throw IllegalStateException(
+    "It was renamed to SaveableStateRegistry and moved to androidx.compose.runtime.saveable" +
+        " package"
+)
+
+@Deprecated(
+    "It was renamed to AmbientSaveableStateRegistry and moved to" +
+        " androidx.compose.runtime.saveable package",
+    ReplaceWith(
+        "AmbientSaveableStateRegistry",
+        "androidx.compose.runtime.saveable.AmbientSaveableStateRegistry"
+    ),
+    level = DeprecationLevel.ERROR
+)
+val AmbientUiSavedStateRegistry = staticAmbientOf<UiSavedStateRegistry?> {
+    throw IllegalStateException(
+        "It was renamed to SaveableStateRegistry and moved to androidx.compose.runtime.saveable" +
+            " package"
+    )
+}
+
+@Deprecated(
+    "It was removed in favor or using rememberSaveable { mutableStateOf(value) }. If you used it " +
+        "with a custom saver you can now pass it like this: rememberSaveable(stateSaver = " +
+        "MySaver) { mutableStateOf(value) }",
+    ReplaceWith(
+        "rememberSaveable { mutableStateOf(init()) }",
+        "androidx.compose.runtime.saveable.rememberSaveable",
+        "androidx.compose.runtime.mutableStateOf"
+    ),
+    level = DeprecationLevel.ERROR
+)
+@Suppress("DocumentExceptions")
+@Composable
+fun <T> savedInstanceState(
+    key: String? = null,
+    init: () -> T
+): MutableState<T> = throw IllegalStateException(
+    "It was removed in favor or using rememberSaveable { mutableStateOf(value) }"
+)
+
+@Deprecated(
+    "It was removed in favor or using rememberSaveable(stateSaver = MySaver) { mutableStateOf" +
+        "(value) }",
+    ReplaceWith(
+        "rememberSaveable(stateSaver = saver) { mutableStateOf(init()) }",
+        "androidx.compose.runtime.saveable.rememberSaveable",
+        "androidx.compose.runtime.mutableStateOf"
+    ),
+    level = DeprecationLevel.ERROR
+)
+@Suppress("DocumentExceptions")
+@Composable
+fun <T> savedInstanceState(
+    saver: Saver<T, out Any>,
+    key: String? = null,
+    init: () -> T
+): MutableState<T> = throw IllegalStateException(
+    "It was removed in favor or using rememberSaveable { mutableStateOf(value) }"
+)
+
+@Deprecated(
+    "It was removed in favor or using rememberSaveable(stateSaver = MySaver) { mutableStateOf" +
+        "(value) }",
+    ReplaceWith(
+        "rememberSaveable(stateSaver = saver) { mutableStateOf(init()) }",
+        "androidx.compose.runtime.saveable.rememberSaveable",
+        "androidx.compose.runtime.mutableStateOf"
+    ),
+    level = DeprecationLevel.ERROR
+)
+@Suppress("DocumentExceptions")
+@Composable
+fun <T> savedInstanceState(
+    saver: androidx.compose.runtime.saveable.Saver<T, out Any>,
+    key: String? = null,
+    init: () -> T
+): MutableState<T> = throw IllegalStateException(
+    "It was removed in favor or using rememberSaveable { mutableStateOf(value) }"
+)
diff --git a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceState.kt b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceState.kt
deleted file mode 100644
index be4afb1..0000000
--- a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceState.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.runtime.savedinstancestate
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.SnapshotMutationPolicy
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.structuralEqualityPolicy
-import androidx.compose.runtime.saveable.Saver
-import androidx.compose.runtime.saveable.autoSaver
-import androidx.compose.runtime.saveable.rememberSaveable
-
-/**
- * Used to introduce a state value of type [T] into a composition.
- *
- * It behaves similarly to `remember { mutableStateOf(...) }`, but the stored value will survive
- * the activity or process recreation using the saved instance state mechanism (for example it
- * happens when the screen is rotated in the Android application).
- *
- * @sample androidx.compose.runtime.savedinstancestate.samples.SavedInstanceStateSample
- *
- * This function works nicely with immutable values as we wrap them into [MutableState] and
- * update the values in this state. If you work with a mutable object and going to update the
- * state of this object instead of recreating it [rememberSaveable] can suit you more.
- *
- * If you use it with types which can be stored inside the Bundle then it will be saved and
- * restored automatically using [autoSaver], otherwise you will need to provide a custom [Saver]
- * implementation via the [saver] param.
- *
- * @sample androidx.compose.runtime.saveable.samples.CustomSaverSample
- *
- * @param inputs A set of inputs such that, when any of them have changed, will cause the state to
- * reset and [init] to be rerun
- * @param saver The [Saver] object which defines how the state is saved and restored.
- * @param key An optional key to be used as a key for the saved value. If not provided we use the
- * automatically generated by the Compose runtime which is unique for the every exact code location
- * in the composition tree
- * @param policy a callback to compare the previous and new instance of [T] when
- * [MutableState.value] is written to. The policy is used to determine how composition should be
- * scheduled. See [SnapshotMutationPolicy].
- * @param init A factory function to create the initial value of this state
- */
-@Composable
-fun <T> savedInstanceState(
-    vararg inputs: Any?,
-    saver: Saver<T, out Any> = autoSaver(),
-    key: String? = null,
-    policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy(),
-    init: () -> T
-): MutableState<T> = rememberSaveable(
-    *inputs,
-    saver = mutableStateSaver(saver, policy),
-    key = key,
-    init = { mutableStateOf(init(), policy) }
-)
-
-private fun <T> mutableStateSaver(
-    inner: Saver<T, out Any>,
-    policy: SnapshotMutationPolicy<T>
-) = Saver<MutableState<T>, Any>(
-    save = { state ->
-        with(inner) {
-            val value = state.value
-            if (value == null) {
-                EmptyStateValue
-            } else {
-                save(value)
-            }
-        }
-    },
-    restore = @Suppress("UNCHECKED_CAST") {
-        val restored = if (it == EmptyStateValue) {
-            null
-        } else {
-            (inner as Saver<T, Any>).restore(it)
-        }
-        mutableStateOf(restored as T, policy)
-    }
-)
-
-/**
- * The object we save to indicate that we will need to restore the state with a null value.
- */
-private val EmptyStateValue = "[NullValuePlacedInsideTheMutableState]"
\ No newline at end of file
diff --git a/compose/runtime/runtime-saveable/src/test/java/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistryTest.kt b/compose/runtime/runtime-saveable/src/test/java/androidx/compose/runtime/saveable/SaveableStateRegistryTest.kt
similarity index 89%
rename from compose/runtime/runtime-saveable/src/test/java/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistryTest.kt
rename to compose/runtime/runtime-saveable/src/test/java/androidx/compose/runtime/saveable/SaveableStateRegistryTest.kt
index 4c34371..1dbda70 100644
--- a/compose/runtime/runtime-saveable/src/test/java/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistryTest.kt
+++ b/compose/runtime/runtime-saveable/src/test/java/androidx/compose/runtime/saveable/SaveableStateRegistryTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.compose.runtime.savedinstancestate
+package androidx.compose.runtime.saveable
 
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
@@ -22,7 +22,7 @@
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-class UiSavedStateRegistryTest {
+class SaveableStateRegistryTest {
 
     @Test
     fun saveSimpleValue() {
@@ -40,8 +40,8 @@
         val registry = createRegistry()
 
         val provider = { 10 }
-        registry.registerProvider("key", provider)
-        registry.unregisterProvider("key", provider)
+        val entry = registry.registerProvider("key", provider)
+        entry.unregister()
 
         registry.performSave().apply {
             assertThat(containsKey("key")).isFalse()
@@ -53,8 +53,8 @@
         val registry = createRegistry()
 
         val provider1 = { "value1" }
-        registry.registerProvider("key", provider1)
-        registry.unregisterProvider("key", provider1)
+        val entry = registry.registerProvider("key", provider1)
+        entry.unregister()
         registry.registerProvider("key") { "value2" }
 
         registry.performSave().apply {
@@ -68,10 +68,10 @@
 
         registry.registerProvider("key1") { 100L }
         val provider2 = { 100 }
-        registry.registerProvider("key2", provider2)
+        val entry = registry.registerProvider("key2", provider2)
         registry.registerProvider("key3") { "value" }
         registry.registerProvider("key4") { listOf("item") }
-        registry.unregisterProvider("key2", provider2)
+        entry.unregister()
 
         registry.performSave().apply {
             assertThat(get("key1")).isEqualTo(listOf(100L))
@@ -180,10 +180,9 @@
         val registry = createRegistry()
 
         registry.registerProvider("key") { 1L }
-        val provider2 = { 2 }
-        registry.registerProvider("key", provider2)
-        registry.registerProvider("key") { 3 }
-        registry.unregisterProvider("key", provider2)
+        val entry = registry.registerProvider("key") { 2L }
+        registry.registerProvider("key") { 3L }
+        entry.unregister()
 
         val restoredRegistry = createRegistry(registry.performSave())
         assertThat(restoredRegistry.consumeRestored("key")).isEqualTo(1L)
@@ -193,5 +192,5 @@
     private fun createRegistry(
         restored: Map<String, List<Any?>>? = null,
         canBeSaved: (Any) -> Boolean = { true }
-    ) = UiSavedStateRegistry(restored, canBeSaved)
-}
\ No newline at end of file
+    ) = SaveableStateRegistry(restored, canBeSaved)
+}
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 0d23c10f..86110fe 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -24,8 +24,8 @@
   }
 
   @androidx.compose.runtime.Stable public abstract sealed class Ambient<T> {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final inline T! getCurrent();
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final inline T! current;
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! getCurrent();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! current;
   }
 
   public final class AmbientKt {
@@ -64,11 +64,11 @@
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface Composable {
   }
 
-  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ComposableContract {
-    method public abstract boolean preventCapture() default false;
-    method public abstract boolean readonly() default false;
-    method public abstract boolean restartable() default true;
-    method public abstract boolean tracked() default true;
+  @Deprecated @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ComposableContract {
+    method @Deprecated public abstract boolean preventCapture() default false;
+    method @Deprecated public abstract boolean readonly() default false;
+    method @Deprecated public abstract boolean restartable() default true;
+    method @Deprecated public abstract boolean tracked() default true;
     property public abstract boolean preventCapture;
     property public abstract boolean readonly;
     property public abstract boolean restartable;
@@ -79,14 +79,14 @@
   }
 
   public final class ComposeKt {
-    method @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
-    method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
+    method @Deprecated @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+    method @Deprecated public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
   }
 
   public final class ComposeNodeKt {
     method @androidx.compose.runtime.Composable public static inline <T extends java.lang.Object, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update);
     method @androidx.compose.runtime.Composable public static inline <T extends java.lang.Object, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update, kotlin.jvm.functions.Function0<? extends kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public static inline <T, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SkippableUpdater<T>,? extends kotlin.Unit> skippableUpdate, kotlin.jvm.functions.Function0<? extends kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static inline <T, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SkippableUpdater<T>,? extends kotlin.Unit> skippableUpdate, kotlin.jvm.functions.Function0<? extends kotlin.Unit> content);
   }
 
   public interface Composer {
@@ -220,6 +220,9 @@
     method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
   }
 
+  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget) public @interface DisallowComposableCalls {
+  }
+
   public interface DisposableEffectResult {
     method public void dispose();
   }
@@ -315,6 +318,9 @@
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NoLiveLiterals {
   }
 
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NonRestartableComposable {
+  }
+
   public final class PausableMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
     ctor public PausableMonotonicFrameClock(androidx.compose.runtime.MonotonicFrameClock frameClock);
     method public boolean isPaused();
@@ -350,12 +356,15 @@
     property public final T! value;
   }
 
+  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ReadOnlyComposable {
+  }
+
   public interface RecomposeScope {
     method public void invalidate();
   }
 
   public final class RecomposeScopeImplKt {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public static androidx.compose.runtime.RecomposeScope getCurrentRecomposeScope();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static androidx.compose.runtime.RecomposeScope getCurrentRecomposeScope();
   }
 
   public final class Recomposer extends androidx.compose.runtime.CompositionReference {
@@ -424,17 +433,17 @@
   }
 
   public final class SideEffectKt {
-    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object? key1, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object![]? keys, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object![]? keys, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
     method @Deprecated @androidx.compose.runtime.Composable public static void LaunchedEffect(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object? key1, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void SideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void SideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
   }
 
   public final inline class SkippableUpdater<T> {
@@ -599,7 +608,7 @@
 
   public final class ComposableLambdaKt {
     method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambda(androidx.compose.runtime.Composer composer, int key, boolean tracked, String? sourceInformation, Object block);
-    method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, Object block);
+    method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, String? sourceInformation, Object block);
   }
 
   @androidx.compose.runtime.ComposeCompilerApi @androidx.compose.runtime.Stable public final class ComposableLambdaN<R> implements kotlin.jvm.functions.FunctionN<R> {
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index 0d23c10f..86110fe 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -24,8 +24,8 @@
   }
 
   @androidx.compose.runtime.Stable public abstract sealed class Ambient<T> {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final inline T! getCurrent();
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final inline T! current;
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! getCurrent();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! current;
   }
 
   public final class AmbientKt {
@@ -64,11 +64,11 @@
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface Composable {
   }
 
-  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ComposableContract {
-    method public abstract boolean preventCapture() default false;
-    method public abstract boolean readonly() default false;
-    method public abstract boolean restartable() default true;
-    method public abstract boolean tracked() default true;
+  @Deprecated @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ComposableContract {
+    method @Deprecated public abstract boolean preventCapture() default false;
+    method @Deprecated public abstract boolean readonly() default false;
+    method @Deprecated public abstract boolean restartable() default true;
+    method @Deprecated public abstract boolean tracked() default true;
     property public abstract boolean preventCapture;
     property public abstract boolean readonly;
     property public abstract boolean restartable;
@@ -79,14 +79,14 @@
   }
 
   public final class ComposeKt {
-    method @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
-    method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
+    method @Deprecated @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+    method @Deprecated public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
   }
 
   public final class ComposeNodeKt {
     method @androidx.compose.runtime.Composable public static inline <T extends java.lang.Object, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update);
     method @androidx.compose.runtime.Composable public static inline <T extends java.lang.Object, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update, kotlin.jvm.functions.Function0<? extends kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public static inline <T, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SkippableUpdater<T>,? extends kotlin.Unit> skippableUpdate, kotlin.jvm.functions.Function0<? extends kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static inline <T, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SkippableUpdater<T>,? extends kotlin.Unit> skippableUpdate, kotlin.jvm.functions.Function0<? extends kotlin.Unit> content);
   }
 
   public interface Composer {
@@ -220,6 +220,9 @@
     method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
   }
 
+  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget) public @interface DisallowComposableCalls {
+  }
+
   public interface DisposableEffectResult {
     method public void dispose();
   }
@@ -315,6 +318,9 @@
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NoLiveLiterals {
   }
 
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NonRestartableComposable {
+  }
+
   public final class PausableMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
     ctor public PausableMonotonicFrameClock(androidx.compose.runtime.MonotonicFrameClock frameClock);
     method public boolean isPaused();
@@ -350,12 +356,15 @@
     property public final T! value;
   }
 
+  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ReadOnlyComposable {
+  }
+
   public interface RecomposeScope {
     method public void invalidate();
   }
 
   public final class RecomposeScopeImplKt {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public static androidx.compose.runtime.RecomposeScope getCurrentRecomposeScope();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static androidx.compose.runtime.RecomposeScope getCurrentRecomposeScope();
   }
 
   public final class Recomposer extends androidx.compose.runtime.CompositionReference {
@@ -424,17 +433,17 @@
   }
 
   public final class SideEffectKt {
-    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object? key1, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object![]? keys, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object![]? keys, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
     method @Deprecated @androidx.compose.runtime.Composable public static void LaunchedEffect(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object? key1, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void SideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void SideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
   }
 
   public final inline class SkippableUpdater<T> {
@@ -599,7 +608,7 @@
 
   public final class ComposableLambdaKt {
     method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambda(androidx.compose.runtime.Composer composer, int key, boolean tracked, String? sourceInformation, Object block);
-    method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, Object block);
+    method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, String? sourceInformation, Object block);
   }
 
   @androidx.compose.runtime.ComposeCompilerApi @androidx.compose.runtime.Stable public final class ComposableLambdaN<R> implements kotlin.jvm.functions.FunctionN<R> {
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index bb6fd9e..11370f5 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -25,8 +25,8 @@
   }
 
   @androidx.compose.runtime.Stable public abstract sealed class Ambient<T> {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final inline T! getCurrent();
-    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public final inline T! current;
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! getCurrent();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T! current;
   }
 
   public final class AmbientKt {
@@ -65,11 +65,11 @@
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface Composable {
   }
 
-  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ComposableContract {
-    method public abstract boolean preventCapture() default false;
-    method public abstract boolean readonly() default false;
-    method public abstract boolean restartable() default true;
-    method public abstract boolean tracked() default true;
+  @Deprecated @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ComposableContract {
+    method @Deprecated public abstract boolean preventCapture() default false;
+    method @Deprecated public abstract boolean readonly() default false;
+    method @Deprecated public abstract boolean restartable() default true;
+    method @Deprecated public abstract boolean tracked() default true;
     property public abstract boolean preventCapture;
     property public abstract boolean readonly;
     property public abstract boolean restartable;
@@ -80,14 +80,14 @@
   }
 
   public final class ComposeKt {
-    method @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
-    method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
+    method @Deprecated @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+    method @Deprecated public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
   }
 
   public final class ComposeNodeKt {
     method @androidx.compose.runtime.Composable public static inline <T extends java.lang.Object, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update);
     method @androidx.compose.runtime.Composable public static inline <T extends java.lang.Object, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update, kotlin.jvm.functions.Function0<? extends kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public static inline <T, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SkippableUpdater<T>,? extends kotlin.Unit> skippableUpdate, kotlin.jvm.functions.Function0<? extends kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static inline <T, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,? extends kotlin.Unit> update, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SkippableUpdater<T>,? extends kotlin.Unit> skippableUpdate, kotlin.jvm.functions.Function0<? extends kotlin.Unit> content);
     method @kotlin.PublishedApi internal static void invalidApplier();
   }
 
@@ -243,6 +243,9 @@
     method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
   }
 
+  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget) public @interface DisallowComposableCalls {
+  }
+
   public interface DisposableEffectResult {
     method public void dispose();
   }
@@ -339,6 +342,9 @@
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NoLiveLiterals {
   }
 
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NonRestartableComposable {
+  }
+
   public final class PausableMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
     ctor public PausableMonotonicFrameClock(androidx.compose.runtime.MonotonicFrameClock frameClock);
     method public boolean isPaused();
@@ -374,12 +380,15 @@
     property public final T! value;
   }
 
+  @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ReadOnlyComposable {
+  }
+
   public interface RecomposeScope {
     method public void invalidate();
   }
 
   public final class RecomposeScopeImplKt {
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(readonly=true) public static androidx.compose.runtime.RecomposeScope getCurrentRecomposeScope();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static androidx.compose.runtime.RecomposeScope getCurrentRecomposeScope();
   }
 
   public final class Recomposer extends androidx.compose.runtime.CompositionReference {
@@ -448,17 +457,17 @@
   }
 
   public final class SideEffectKt {
-    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object? key1, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void DisposableEffect(Object![]? keys, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object![]? keys, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
     method @Deprecated @androidx.compose.runtime.Composable public static void LaunchedEffect(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object? key1, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void LaunchedEffect(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableContract(restartable=false) public static void SideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void SideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
   }
 
   public final inline class SkippableUpdater<T> {
@@ -626,7 +635,7 @@
 
   public final class ComposableLambdaKt {
     method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambda(androidx.compose.runtime.Composer composer, int key, boolean tracked, String? sourceInformation, Object block);
-    method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, Object block);
+    method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, String? sourceInformation, Object block);
   }
 
   @androidx.compose.runtime.ComposeCompilerApi @androidx.compose.runtime.Stable public final class ComposableLambdaN<R> implements kotlin.jvm.functions.FunctionN<R> {
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
index dc13ec6..c4c19d9 100644
--- a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
@@ -501,13 +501,12 @@
         narrowInvalidateForReference(ref = ref)
         return {
             @OptIn(ExperimentalComposeApi::class)
-            // TODO(b/150390669): Review use of @ComposableContract(tracked = false)
             Composition(
                 container,
                 UiApplier(container),
                 ref.value
             ).apply {
-                setContent @ComposableContract(tracked = false) {
+                setContent {
                     block()
                 }
             }
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
index b4da533..21005f4 100644
--- a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
@@ -104,14 +104,13 @@
     fun subCompose(block: @Composable () -> Unit) {
         val container = remember { View(activity) }
         val reference = rememberCompositionReference()
-        // TODO(b/150390669): Review use of @ComposableContract(tracked = false)
         @OptIn(ExperimentalComposeApi::class)
         Composition(
             container,
             UiApplier(container),
             reference
         ).apply {
-            setContent @ComposableContract(tracked = false) {
+            setContent {
                 block()
             }
         }
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeIntoTests.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeIntoTests.kt
index 882f819..3c8130f 100644
--- a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeIntoTests.kt
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeIntoTests.kt
@@ -51,7 +51,7 @@
 
         var initializationCount = 0
         @OptIn(ExperimentalComposeApi::class)
-        val composable = @Composable @ComposableContract(tracked = false) {
+        val composable = @Composable {
             DisposableEffect(Unit) {
                 initializationCount++
                 onDispose { }
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/DisposeTests.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/DisposeTests.kt
index b3552d8..80f82f6 100644
--- a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/DisposeTests.kt
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/DisposeTests.kt
@@ -48,7 +48,7 @@
 
         lateinit var recomposeScope: RecomposeScope
         @OptIn(ExperimentalComposeApi::class)
-        val composable = @Composable @ComposableContract(tracked = false) {
+        val composable = @Composable {
             recomposeScope = currentRecomposeScope
             DisposableEffect(NeverEqualObject) {
                 log.add("onCommit")
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/RestartTests.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/RestartTests.kt
index 37c8dd2..1140c1f 100644
--- a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/RestartTests.kt
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/RestartTests.kt
@@ -248,7 +248,7 @@
 }
 
 @Composable
-@ComposableContract(restartable = false)
+@NonRestartableComposable
 fun DirectNothing() {
 }
 
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Ambient.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Ambient.kt
index 53abb5f..1ab3d8e 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Ambient.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Ambient.kt
@@ -70,7 +70,7 @@
      */
     @OptIn(InternalComposeApi::class)
     inline val current: T
-        @ComposableContract(readonly = true)
+        @ReadOnlyComposable
         @Composable
         get() = currentComposer.consume(this)
 }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposableContract.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposableContract.kt
index 213e507..dfbefbb 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposableContract.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposableContract.kt
@@ -43,6 +43,13 @@
  */
 @MustBeDocumented
 @Retention(AnnotationRetention.BINARY)
+@Deprecated(
+    "ComposableContract has been replaced with several different annotations.\n" +
+        "@ComposableContract(restartable = false) has become @NonRestartableComposable\n" +
+        "@ComposableContract(readonly = true) has become @ReadOnlyComposable\n" +
+        "@ComposableContract(preventCapture = true) has become @DisallowComposableCalls\n" +
+        "@ComposableContract(tracked = true) has been removed."
+)
 @Target(
     AnnotationTarget.FUNCTION,
     AnnotationTarget.PROPERTY, // (DEPRECATED)
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt
index c042129..68f5550 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt
@@ -24,10 +24,18 @@
  * See [orEmpty] for handling nullable Composable lambdas using empty content.
  */
 @Stable
+@Deprecated(
+    "An empty lambda literal is preferred",
+    replaceWith = ReplaceWith("{}")
+)
 fun emptyContent() = EmptyComposable
 
 /**
  * @return this Composable if not null, else [emptyContent].
  */
-@Suppress("NOTHING_TO_INLINE")
+@Suppress("NOTHING_TO_INLINE", "DEPRECATION")
+@Deprecated(
+    "Use the null coalescing operator instead",
+    replaceWith = ReplaceWith("this ?: {}")
+)
 inline fun (@Composable (() -> Unit))?.orEmpty() = this ?: emptyContent()
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeNode.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeNode.kt
index 139a7bd..3a17aca 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeNode.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeNode.kt
@@ -37,7 +37,7 @@
 @OptIn(ComposeCompilerApi::class)
 @Composable inline fun <T : Any, reified E : Applier<*>> ComposeNode(
     noinline factory: () -> T,
-    update: @ComposableContract(preventCapture = true) Updater<T>.() -> Unit
+    update: @DisallowComposableCalls Updater<T>.() -> Unit
 ) {
     if (currentComposer.applier !is E) invalidApplier()
     currentComposer.startNode()
@@ -73,7 +73,7 @@
 @Composable
 inline fun <T : Any?, reified E : Applier<*>> ComposeNode(
     noinline factory: () -> T,
-    update: @ComposableContract(preventCapture = true) Updater<T>.() -> Unit,
+    update: @DisallowComposableCalls Updater<T>.() -> Unit,
     content: @Composable () -> Unit
 ) {
     if (currentComposer.applier !is E) invalidApplier()
@@ -114,10 +114,10 @@
  * @see Composition
  */
 @OptIn(ComposeCompilerApi::class)
-@Composable @ComposableContract(readonly = true)
+@Composable @ReadOnlyComposable
 inline fun <T, reified E : Applier<*>> ComposeNode(
     noinline factory: () -> T,
-    update: @ComposableContract(preventCapture = true) Updater<T>.() -> Unit,
+    update: @DisallowComposableCalls Updater<T>.() -> Unit,
     noinline skippableUpdate: @Composable SkippableUpdater<T>.() -> Unit,
     content: @Composable () -> Unit
 ) {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
index b7b078a..8435ff1 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
@@ -21,6 +21,7 @@
 )
 package androidx.compose.runtime
 
+import androidx.compose.runtime.collection.IdentityScopeMap
 import androidx.compose.runtime.tooling.InspectionTables
 import kotlinx.collections.immutable.PersistentMap
 import kotlinx.collections.immutable.persistentHashMapOf
@@ -959,8 +960,8 @@
     private var collectKeySources = false
     private var collectParameterInformation = false
     private var nodeExpected = false
-    private val observations: MutableList<Any> = mutableListOf()
-    private val observationsProcessed: MutableList<Any> = mutableListOf()
+    private val observations = IdentityScopeMap<RecomposeScopeImpl>()
+    private val observationsProcessed = IdentityScopeMap<RecomposeScopeImpl>()
     private val invalidations: MutableList<Invalidation> = mutableListOf()
     internal var pendingInvalidScopes = false
     private val entersStack = IntStack()
@@ -1231,7 +1232,7 @@
         if (childrenComposing == 0) {
             currentRecomposeScope?.let {
                 it.used = true
-                observations.insertIfMissing(value, it)
+                observations.add(value, it)
             }
         }
     }
@@ -1248,7 +1249,7 @@
         observations.forEachScopeOf(value) { scope ->
             if (scope.invalidateForResult() == InvalidationResult.IMMINENT) {
                 // If we process this during recordWriteOf, ignore it when recording modifications
-                observationsProcessed.insertIfMissing(value, scope)
+                observationsProcessed.add(value, scope)
             }
         }
     }
@@ -1287,7 +1288,7 @@
         var invalidated: HashSet<RecomposeScopeImpl>? = null
         for (value in values) {
             observations.forEachScopeOf(value) { scope ->
-                if (!observationsProcessed.removeValueScope(value, scope) &&
+                if (!observationsProcessed.remove(value, scope) &&
                     scope.invalidateForResult() != InvalidationResult.IGNORED
                 ) {
                     (
@@ -1301,7 +1302,7 @@
             }
         }
         invalidated?.let {
-            observations.removeValueIf { _, scope -> scope in it }
+            observations.removeValueIf { scope -> scope in it }
         }
     }
 
@@ -1429,7 +1430,7 @@
 
                 if (pendingInvalidScopes) {
                     pendingInvalidScopes = false
-                    observations.removeValueIf { _, scope -> !scope.valid }
+                    observations.removeValueIf { scope -> !scope.valid }
                 }
             } finally {
                 manager.dispatchAbandons()
@@ -3377,55 +3378,6 @@
     return false
 }
 
-private inline fun MutableList<Any>.removeValueIf(
-    predicate: (value: Any, scope: RecomposeScopeImpl) -> Boolean
-) {
-    var copyLocation = 0
-    for (index in 0 until size / 2) {
-        val slot = index * 2
-        val value = get(slot)
-        val scope = get(slot + 1) as RecomposeScopeImpl
-        if (!predicate(value, scope)) {
-            if (copyLocation != slot) {
-                // Keep the value by copying over a value that has been moved or removed.
-                set(copyLocation++, value)
-                set(copyLocation++, scope)
-            } else {
-                // No slots have been removed yet, just update the copy location
-                copyLocation += 2
-            }
-        }
-    }
-    if (copyLocation < size) {
-        // Delete any left-over slots.
-        subList(copyLocation, size).clear()
-    }
-}
-
-/**
- * Iterate through all the scopes associated with [value]. Returns `false` if [value] has no scopes
- * associated with it.
- */
-private inline fun MutableList<Any>.forEachScopeOf(
-    value: Any,
-    block: (scope: RecomposeScopeImpl) -> Unit
-): Boolean {
-    val valueHash = identityHashCode(value)
-    var index = findFirst(valueHash)
-    var result = false
-    while (index < size) {
-        val storedValue = get(index)
-        if (identityHashCode(storedValue) != valueHash) break
-        if (storedValue === value) {
-            val storedScope = get(index + 1) as RecomposeScopeImpl
-            block(storedScope)
-            result = true
-        }
-        index += 2
-    }
-    return result
-}
-
 private fun MutableList<Any>.find(value: Any, scope: RecomposeScopeImpl): Int {
     val valueHash = identityHashCode(value)
     val scopeHash = identityHashCode(scope)
@@ -3445,9 +3397,6 @@
     return -(index + 1)
 }
 
-private fun MutableList<Any>.findFirst(valueHash: Int) =
-    find(valueHash, 0).let { if (it < 0) -(it + 1) else it }
-
 private fun MutableList<Any>.find(valueHash: Int, scopeHash: Int): Int {
     var low = 0
     var high = (size / 2) - 1
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
index 2d3cbee..7152d2a 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
@@ -258,7 +258,7 @@
 
     private var disposed = false
 
-    var composable: @Composable () -> Unit = emptyContent()
+    var composable: @Composable () -> Unit = {}
 
     override val isComposing: Boolean
         get() = composer.isComposing
@@ -282,7 +282,7 @@
     override fun dispose() {
         if (!disposed) {
             disposed = true
-            composable = emptyContent()
+            composable = {}
             composer.dispose()
             parent.unregisterComposition(this)
             onDispose?.invoke()
@@ -370,7 +370,7 @@
             state.clear()
             val holders = Compositions.collectAll()
             holders.mapTo(state) { it to it.composable }
-            holders.filter { it.isRoot }.forEach { it.setContent(emptyContent()) }
+            holders.filter { it.isRoot }.forEach { it.setContent({}) }
         }
 
         // Called after Dex Code Swap
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DisallowComposableCalls.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DisallowComposableCalls.kt
new file mode 100644
index 0000000..8d9ba3c
--- /dev/null
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DisallowComposableCalls.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime
+
+/**
+ * This will prevent composable calls from happening inside of the
+ * function that it applies to. This is usually applied to lambda parameters of inline composable
+ * functions that ought to be inlined but cannot safely have composable calls in them.
+ */
+@MustBeDocumented
+@Retention(AnnotationRetention.BINARY)
+@Target(AnnotationTarget.TYPE)
+annotation class DisallowComposableCalls
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/NonRestartableComposable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/NonRestartableComposable.kt
new file mode 100644
index 0000000..6003162
--- /dev/null
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/NonRestartableComposable.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime
+
+/**
+ * This annotation can be applied to [Composable] functions in order to prevent code from being
+ * generated which allow this function's execution to be skipped or restarted. This may be
+ * desirable for small functions which just directly call another composable function and have
+ * very little machinery in them directly, and are unlikely to be invalidated themselves.
+ */
+@Retention(AnnotationRetention.SOURCE)
+@Target(
+    AnnotationTarget.FUNCTION,
+    AnnotationTarget.PROPERTY_GETTER
+)
+annotation class NonRestartableComposable
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ReadOnlyComposable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ReadOnlyComposable.kt
new file mode 100644
index 0000000..2bdb54c
--- /dev/null
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ReadOnlyComposable.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime
+
+/**
+ * This annotation can be applied to [Composable] functions so that no group will be generated
+ * around the body of the function it annotates. This is not safe unless the body of the function
+ * and any functions that it calls only executes "read" operations on the passed in composer.
+ * This will result in slightly more efficient code.
+ *
+ * A common use case for this are for functions that only need to be composable in order to read
+ * [Ambient] values, but don't call any other composables.
+ *
+ * Caution: Use of this annotation means that the annotated declaration *MUST* comply with this
+ * contract, or else the resulting code's behavior will be undefined.
+ */
+@MustBeDocumented
+@Retention(AnnotationRetention.BINARY)
+@Target(
+    AnnotationTarget.FUNCTION,
+    AnnotationTarget.PROPERTY_GETTER
+)
+annotation class ReadOnlyComposable
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/RecomposeScopeImpl.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/RecomposeScopeImpl.kt
index 84a2b8c..b549625 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/RecomposeScopeImpl.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/RecomposeScopeImpl.kt
@@ -32,7 +32,7 @@
  * This object can be used to manually cause recompositions.
  */
 val currentRecomposeScope: RecomposeScope
-    @ComposableContract(readonly = true)
+    @ReadOnlyComposable
     @OptIn(InternalComposeApi::class)
     @Composable get() {
         val scope = currentComposer.recomposeScope ?: error("no recompose scope found")
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Remember.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Remember.kt
index 062b3b5..b52a33c 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Remember.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Remember.kt
@@ -22,7 +22,7 @@
  */
 @OptIn(ComposeCompilerApi::class)
 @Composable
-inline fun <T> remember(calculation: @ComposableContract(preventCapture = true) () -> T): T =
+inline fun <T> remember(calculation: @DisallowComposableCalls () -> T): T =
     currentComposer.cache(false, calculation)
 
 /**
@@ -33,7 +33,7 @@
 @Composable
 inline fun <T> remember(
     key1: Any?,
-    calculation: @ComposableContract(preventCapture = true) () -> T
+    calculation: @DisallowComposableCalls () -> T
 ): T {
     return currentComposer.cache(currentComposer.changed(key1), calculation)
 }
@@ -47,7 +47,7 @@
 inline fun <T> remember(
     key1: Any?,
     key2: Any?,
-    calculation: @ComposableContract(preventCapture = true) () -> T
+    calculation: @DisallowComposableCalls () -> T
 ): T {
     return currentComposer.cache(
         currentComposer.changed(key1) or currentComposer.changed(key2),
@@ -65,7 +65,7 @@
     key1: Any?,
     key2: Any?,
     key3: Any?,
-    calculation: @ComposableContract(preventCapture = true) () -> T
+    calculation: @DisallowComposableCalls () -> T
 ): T {
     return currentComposer.cache(
         currentComposer.changed(key1) or
@@ -83,7 +83,7 @@
 @Composable
 inline fun <T> remember(
     vararg keys: Any?,
-    calculation: @ComposableContract(preventCapture = true) () -> T
+    calculation: @DisallowComposableCalls () -> T
 ): T {
     var invalid = false
     for (key in keys) invalid = invalid or currentComposer.changed(key)
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SideEffect.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SideEffect.kt
index 20a9827..60380c6 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SideEffect.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SideEffect.kt
@@ -39,7 +39,7 @@
  * object lifecycle, see [DisposableEffect].
  */
 @Composable
-@ComposableContract(restartable = false)
+@NonRestartableComposable
 @OptIn(InternalComposeApi::class)
 fun SideEffect(
     effect: () -> Unit
@@ -101,7 +101,7 @@
         "and a new effect launched for the new key."
 
 @Composable
-@ComposableContract(restartable = false)
+@NonRestartableComposable
 @Suppress("DeprecatedCallableAddReplaceWith", "UNUSED_PARAMETER")
 @Deprecated(DisposableEffectNoParamError, level = DeprecationLevel.ERROR)
 fun DisposableEffect(
@@ -138,7 +138,7 @@
  * callbacks.
  */
 @Composable
-@ComposableContract(restartable = false)
+@NonRestartableComposable
 fun DisposableEffect(
     key1: Any?,
     effect: DisposableEffectScope.() -> DisposableEffectResult
@@ -177,7 +177,7 @@
  * event callbacks.
  */
 @Composable
-@ComposableContract(restartable = false)
+@NonRestartableComposable
 fun DisposableEffect(
     key1: Any?,
     key2: Any?,
@@ -217,7 +217,7 @@
  * callbacks.
  */
 @Composable
-@ComposableContract(restartable = false)
+@NonRestartableComposable
 fun DisposableEffect(
     key1: Any?,
     key2: Any?,
@@ -258,7 +258,7 @@
  * callbacks.
  */
 @Composable
-@ComposableContract(restartable = false)
+@NonRestartableComposable
 @Suppress("ArrayReturn")
 fun DisposableEffect(
     vararg keys: Any?,
@@ -316,7 +316,7 @@
  * scoped to the composition in response to event callbacks.
  */
 @Composable
-@ComposableContract(restartable = false)
+@NonRestartableComposable
 @OptIn(InternalComposeApi::class)
 fun LaunchedEffect(
     key1: Any?,
@@ -338,7 +338,7 @@
  * scoped to the composition in response to event callbacks.
  */
 @Composable
-@ComposableContract(restartable = false)
+@NonRestartableComposable
 @OptIn(InternalComposeApi::class)
 fun LaunchedEffect(
     key1: Any?,
@@ -361,7 +361,7 @@
  * scoped to the composition in response to event callbacks.
  */
 @Composable
-@ComposableContract(restartable = false)
+@NonRestartableComposable
 @OptIn(InternalComposeApi::class)
 fun LaunchedEffect(
     key1: Any?,
@@ -385,7 +385,7 @@
  * scoped to the composition in response to event callbacks.
  */
 @Composable
-@ComposableContract(restartable = false)
+@NonRestartableComposable
 @Suppress("ArrayReturn")
 @OptIn(InternalComposeApi::class)
 fun LaunchedEffect(
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
index 5e564eb..eca6421 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
@@ -107,7 +107,7 @@
     /**
      * Remove [value] from the set.
      */
-    fun remove(value: T) {
+    fun remove(value: T): Boolean {
         val index = find(value)
         if (index >= 0) {
             if (index < size - 1) {
@@ -120,7 +120,9 @@
             }
             size--
             values[size] = null
+            return true
         }
+        return false
     }
 
     /**
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
index 6c8ec16..8a9cec8 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
@@ -174,10 +174,44 @@
     }
 
     /**
+     * Remove [scope] from the scope set for [value]. If the scope set is empty after [scope] has
+     * been remove the reference to [value] is removed as well.
+     *
+     * @param value the key of the scope map
+     * @param scope the scope being removed
+     * @return true if the value was removed from the scope
+     */
+    fun remove(value: Any, scope: T): Boolean {
+        val index = find(value)
+        if (index >= 0) {
+            val valueOrderIndex = valueOrder[index]
+            val set = scopeSets[valueOrderIndex] ?: return false
+            val removed = set.remove(scope)
+            if (set.size == 0) {
+                val startIndex = index + 1
+                val endIndex = size
+                if (startIndex < endIndex) {
+                    valueOrder.copyInto(
+                        destination = valueOrder,
+                        destinationOffset = index,
+                        startIndex = startIndex,
+                        endIndex = endIndex
+                    )
+                }
+                valueOrder[size - 1] = valueOrderIndex
+                values[valueOrderIndex] = null
+                size--
+            }
+            return removed
+        }
+        return false
+    }
+
+    /**
      * Removes all scopes that match [predicate]. If all scopes for a given value have been
      * removed, that value is removed also.
      */
-    inline fun removeValueIf(predicate: (scope: Any) -> Boolean) {
+    inline fun removeValueIf(predicate: (scope: T) -> Boolean) {
         var destinationIndex = 0
         for (i in 0 until size) {
             val valueIndex = valueOrder[i]
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt
index 562802f..d1583aa 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt
@@ -1210,5 +1210,5 @@
 
 @Suppress("unused")
 @ComposeCompilerApi
-fun composableLambdaInstance(key: Int, tracked: Boolean, block: Any) =
-    CLambda(key, tracked, null).apply { update(block) }
+fun composableLambdaInstance(key: Int, tracked: Boolean, sourceInformation: String?, block: Any) =
+    CLambda(key, tracked, sourceInformation).apply { update(block) }
diff --git a/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt b/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
index 6da8ac9b..9a4ddf3 100644
--- a/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
+++ b/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
@@ -17,9 +17,6 @@
 package androidx.compose.runtime
 
 import kotlinx.coroutines.delay
-import kotlinx.coroutines.yield
-import java.awt.DisplayMode
-import java.awt.GraphicsEnvironment
 
 internal actual object Trace {
     actual fun beginSection(name: String): Any? {
@@ -93,29 +90,42 @@
 actual annotation class MainThread()
 actual annotation class CheckResult(actual val suggest: String)
 
-// TODO implement local Recomposer in each Window, so each Window can have own MonotonicFrameClock.
-//  It is needed for smooth animations and for the case when user have multiple windows on multiple
-//  monitors with different refresh rates.
-//  see https://github.com/JetBrains/compose-jb/issues/137
+/**
+ * Clock with fixed delay between frames (16ms), independent from any display/window.
+ *
+ * It is used by [withFrameNanos] and [withFrameMillis] if one is not present
+ * in the calling [kotlin.coroutines.CoroutineContext].
+ *
+ * Use it only where you don't need to show animation in a window.
+ *
+ * If you need a frame clock for changing the state of an animation that should be displayed to
+ * user, use [MonotonicFrameClock] that is bound to the current window. You can access it using
+ * [LaunchedEffect]:
+ * ```
+ * LaunchedEffect {
+ *   val frameClock = coroutineContext[MonotonicFrameClock]
+ * }
+ * ```
+ *
+ * Or using [rememberCoroutineScope]:
+ * ```
+ * val scope = rememberCoroutineScope()
+ * val frameClock = scope.coroutineContext[MonotonicFrameClock]
+ * ```
+ *
+ * If [withFrameNanos] / [withFrameMillis] runs inside the coroutine scope
+ * obtained using [LaunchedEffect] or [rememberCoroutineScope] they also use
+ * [MonotonicFrameClock] which is bound to the current window.
+ */
 actual val DefaultMonotonicFrameClock: MonotonicFrameClock by lazy {
     object : MonotonicFrameClock {
+        private val fps = 60
+
         override suspend fun <R> withFrameNanos(
             onFrame: (Long) -> R
         ): R {
-            if (GraphicsEnvironment.isHeadless()) {
-                yield()
-            } else {
-                delay(1000L / getFramesPerSecond())
-            }
+            delay(1000L / fps)
             return onFrame(System.nanoTime())
         }
-
-        private fun getFramesPerSecond(): Int {
-            val refreshRate = GraphicsEnvironment
-                .getLocalGraphicsEnvironment()
-                .screenDevices.maxOfOrNull { it.displayMode.refreshRate }
-                ?: DisplayMode.REFRESH_RATE_UNKNOWN
-            return if (refreshRate != DisplayMode.REFRESH_RATE_UNKNOWN) refreshRate else 60
-        }
     }
-}
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
index 6984a2d..8397eda 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.identityHashCode
 import kotlin.test.Test
 import kotlin.test.assertEquals
+import kotlin.test.assertFalse
 import kotlin.test.assertNotEquals
 import kotlin.test.assertNotSame
 import kotlin.test.assertNull
@@ -89,8 +90,9 @@
         list.forEach { set.add(it) }
 
         // remove a value that doesn't exist:
-        set.remove(Stuff(10))
+        val removed = set.remove(Stuff(10))
         assertEquals(list.size, set.size)
+        assertFalse(removed)
 
         // remove a value in the middle:
         testRemoveValueAtIndex(set.size / 2)
@@ -131,8 +133,9 @@
     private fun testRemoveValueAtIndex(index: Int) {
         val value = set[index]
         val initialSize = set.size
-        set.remove(value)
+        val removed = set.remove(value)
         assertEquals(initialSize - 1, set.size)
+        assertTrue(removed)
         assertNull(set.values[set.size])
         set.forEach { assertNotSame(value, it) }
     }
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
index 4e451ad..391a571 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
@@ -16,8 +16,12 @@
 
 package androidx.compose.runtime.collection
 
+import org.junit.After
 import kotlin.test.Test
 import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
 import kotlin.test.assertTrue
 import kotlin.test.fail
 
@@ -84,6 +88,25 @@
     }
 
     @Test
+    fun removeScope() {
+        val valueC = "C"
+        map.add(valueList[0], scopeList[0])
+        map.add(valueList[0], scopeList[1])
+        map.add(valueList[1], scopeList[2])
+        map.add(valueC, scopeList[3])
+
+        // remove a non existent value, should leave the map unmodified
+        val removed1 = map.remove(valueC, scopeList[0])
+        assertFalse(removed1)
+        assertEquals(3, map.size)
+
+        // Remove a reference which should remove the value
+        val removed2 = map.remove(valueList[1], scopeList[2])
+        assertTrue(removed2)
+        assertEquals(2, map.size)
+    }
+
+    @Test
     fun removeValueIf() {
         val valueC = "C"
         map.add(valueList[0], scopeList[0])
@@ -109,5 +132,38 @@
         }
     }
 
+    /**
+     * Validate the test maintains the internal assumptions of the map.
+     */
+    @After
+    fun validateMap() {
+        // Ensure that no duplicates exist in value-order and all indexes are represented
+        val pendingRepresentation = mutableSetOf(*map.values.indices.toList().toTypedArray())
+        map.valueOrder.forEach {
+            assertTrue(it in pendingRepresentation, "Index $it was duplicated")
+            pendingRepresentation.remove(it)
+        }
+        assertTrue(pendingRepresentation.isEmpty(), "Not all indexes are in the valueOrder map")
+
+        // Ensure values are non-null and sets are not empty for index < size and values are
+        // null and sets are empty or missing for >= size
+        val size = map.size
+        map.valueOrder.forEachIndexed { index, order ->
+            val value = map.values[order]
+            val set = map.scopeSets[order]
+            if (index < size) {
+                assertNotNull(value, "A value was unexpectedly null")
+                assertNotNull(set, "A set was unexpectedly null")
+                assertTrue(set.size > 0, "An empty set wasn't collected")
+            } else {
+                assertNull(value, "A reference to a removed value was retained")
+                assertTrue(
+                    actual = set == null || set.size == 0,
+                    message = "A non-empty set was dropped"
+                )
+            }
+        }
+    }
+
     data class Scope(val item: Int)
 }
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt
index ec91230..f4f16a0 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt
@@ -17,8 +17,8 @@
 package androidx.compose.runtime.mock
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposableContract
 import androidx.compose.runtime.ComposeNode
+import androidx.compose.runtime.NonRestartableComposable
 import androidx.compose.runtime.key
 
 @Composable
@@ -43,7 +43,7 @@
     }
 }
 
-@Composable @ComposableContract(restartable = false)
+@Composable @NonRestartableComposable
 fun Text(value: String) {
     ComposeNode<View, ViewApplier>(
         factory = { View().also { it.name = "text" } },
diff --git a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt
index 59e82e4..6aae00c 100644
--- a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt
+++ b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt
@@ -24,7 +24,7 @@
         top,
         right,
         bottom
-    )!!
+    )
 }
 
 fun org.jetbrains.skija.Rect.toComposeRect() =
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/StateRestorationTester.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/StateRestorationTester.kt
index 56f5670..e49c1eb 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/StateRestorationTester.kt
+++ b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/StateRestorationTester.kt
@@ -21,9 +21,9 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.AmbientUiSavedStateRegistry
+import androidx.compose.runtime.saveable.AmbientSaveableStateRegistry
+import androidx.compose.runtime.saveable.SaveableStateRegistry
 import androidx.compose.runtime.setValue
-import androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry
 
 /**
  * Helps to test the state restoration for your Composable component.
@@ -33,7 +33,7 @@
  * [emulateSavedInstanceStateRestore] and assert your state is restored properly.
  *
  * Note that this tests only the restoration of the local state of the composable you passed to
- * [setContent] and useful for testing [savedInstanceState] or [rememberSaveable]
+ * [setContent] and useful for testing [androidx.compose.runtime.saveable.rememberSaveable]
  * integration. It is not testing the integration with any other life cycles or Activity callbacks.
  */
 class StateRestorationTester(private val composeTestRule: ComposeContentTestRule) {
@@ -78,24 +78,24 @@
 
     @Composable
     private fun InjectRestorationRegistry(content: @Composable (RestorationRegistry) -> Unit) {
-        val original = requireNotNull(AmbientUiSavedStateRegistry.current) {
+        val original = requireNotNull(AmbientSaveableStateRegistry.current) {
             "StateRestorationTester requires composeTestRule.setContent() to provide " +
-                "an UiSavedStateRegistry implementation via UiSavedStateRegistryAmbient"
+                "an SaveableStateRegistry implementation via AmbientSaveableStateRegistry"
         }
         val restorationRegistry = remember { RestorationRegistry(original) }
-        Providers(AmbientUiSavedStateRegistry provides restorationRegistry) {
+        Providers(AmbientSaveableStateRegistry provides restorationRegistry) {
             if (restorationRegistry.shouldEmitChildren) {
                 content(restorationRegistry)
             }
         }
     }
 
-    private class RestorationRegistry(private val original: UiSavedStateRegistry) :
-        UiSavedStateRegistry {
+    private class RestorationRegistry(private val original: SaveableStateRegistry) :
+        SaveableStateRegistry {
 
         var shouldEmitChildren by mutableStateOf(true)
             private set
-        private var currentRegistry: UiSavedStateRegistry = original
+        private var currentRegistry: SaveableStateRegistry = original
         private var savedMap: Map<String, List<Any?>> = emptyMap()
 
         fun saveStateAndDisposeChildren() {
@@ -104,7 +104,7 @@
         }
 
         fun emitChildrenWithRestoredState() {
-            currentRegistry = UiSavedStateRegistry(
+            currentRegistry = SaveableStateRegistry(
                 restoredValues = savedMap,
                 canBeSaved = { original.canBeSaved(it) }
             )
@@ -116,11 +116,8 @@
         override fun registerProvider(key: String, valueProvider: () -> Any?) =
             currentRegistry.registerProvider(key, valueProvider)
 
-        override fun unregisterProvider(key: String, valueProvider: () -> Any?) =
-            currentRegistry.unregisterProvider(key, valueProvider)
-
         override fun canBeSaved(value: Any) = currentRegistry.canBeSaved(value)
 
         override fun performSave() = currentRegistry.performSave()
     }
-}
\ No newline at end of file
+}
diff --git a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
index 462a2ee..016e7c5 100644
--- a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
+++ b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
@@ -18,12 +18,9 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.ExperimentalComposeApi
-import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.ui.node.RootForTest
-import androidx.compose.ui.platform.DesktopOwner
-import androidx.compose.ui.platform.DesktopOwners
-import androidx.compose.ui.platform.setContent
+import androidx.compose.ui.platform.TestComposeWindow
 import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.IdlingResource
@@ -38,11 +35,11 @@
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
-import org.jetbrains.skija.Surface
+import kotlinx.coroutines.swing.Swing
 import org.junit.runner.Description
 import org.junit.runners.model.Statement
-import java.util.LinkedList
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.FutureTask
 import javax.swing.SwingUtilities.invokeAndWait
@@ -57,18 +54,15 @@
         var current: DesktopComposeTestRule? = null
     }
 
-    var owners: DesktopOwners? = null
-    private var owner: DesktopOwner? = null
-
     override val density: Density
-        get() = TODO()
+        get() = Density(1f, 1f)
 
     override val mainClock: MainTestClock
         get() = TODO()
 
     internal val testDisplaySize: IntSize get() = IntSize(1024, 768)
 
-    val executionQueue = LinkedList<() -> Unit>()
+    lateinit var window: TestComposeWindow
 
     private val testOwner = DesktopTestOwner(this)
     private val testContext = createTestContext(testOwner)
@@ -77,30 +71,32 @@
         current = this
         return object : Statement() {
             override fun evaluate() {
-                base.evaluate()
-                runExecutionQueue()
-                runOnUiThread {
-                    owner?.dispose()
-                    owner = null
+                window = runOnUiThread(::createWindow)
+
+                try {
+                    base.evaluate()
+                } finally {
+                    runOnUiThread(window::dispose)
                 }
             }
         }
     }
 
-    private fun runExecutionQueue() {
-        while (executionQueue.isNotEmpty()) {
-            executionQueue.removeFirst()()
-        }
-    }
+    private fun createWindow() = TestComposeWindow(
+        width = testDisplaySize.width,
+        height = testDisplaySize.height,
+        density = density,
+        nanoTime = System::nanoTime, // TODO(demin): use mainClock?
+        coroutineContext = Dispatchers.Swing
+    )
 
     @OptIn(ExperimentalComposeApi::class)
     private fun isIdle() =
         !Snapshot.current.hasPendingChanges() &&
-            !Recomposer.runningRecomposers.value.any { it.hasPendingWork }
+            !window.hasInvalidations()
 
     override fun waitForIdle() {
         while (!isIdle()) {
-            runExecutionQueue()
             Thread.sleep(10)
         }
     }
@@ -108,18 +104,21 @@
     @ExperimentalTestApi
     override suspend fun awaitIdle() {
         while (!isIdle()) {
-            runExecutionQueue()
             delay(10)
         }
     }
 
     override fun <T> runOnUiThread(action: () -> T): T {
-        val task: FutureTask<T> = FutureTask(action)
-        invokeAndWait(task)
-        try {
-            return task.get()
-        } catch (e: ExecutionException) { // Expose the original exception
-            throw e.cause!!
+        return if (isEventDispatchThread()) {
+            action()
+        } else {
+            val task: FutureTask<T> = FutureTask(action)
+            invokeAndWait(task)
+            try {
+                return task.get()
+            } catch (e: ExecutionException) { // Expose the original exception
+                throw e.cause!!
+            }
         }
     }
 
@@ -145,15 +144,11 @@
     }
 
     override fun setContent(composable: @Composable () -> Unit) {
-        check(owner == null) {
-            "Cannot call setContent twice per test!"
-        }
-
         if (isEventDispatchThread()) {
-            performSetContent(composable)
+            window.setContent(composable)
         } else {
             runOnUiThread {
-                performSetContent(composable)
+                window.setContent(composable)
             }
 
             // Only wait for idleness if not on the UI thread. If we are on the UI thread, the
@@ -163,20 +158,6 @@
         }
     }
 
-    private fun performSetContent(composable: @Composable() () -> Unit) {
-        val surface = Surface.makeRasterN32Premul(testDisplaySize.width, testDisplaySize.height)!!
-        val canvas = surface.canvas
-        val owners = DesktopOwners(invalidate = {}).also {
-            owners = it
-        }
-        val owner = DesktopOwner(owners)
-        owner.setContent(content = composable)
-        owner.setSize(testDisplaySize.width, testDisplaySize.height)
-        owner.measureAndLayout()
-        owner.draw(canvas)
-        this.owner = owner
-    }
-
     override fun onNode(
         matcher: SemanticsMatcher,
         useUnmergedTree: Boolean
@@ -205,7 +186,7 @@
         }
 
         override fun getRoots(): Set<RootForTest> {
-            return rule.owners!!.list
+            return rule.window.roots
         }
 
         override val mainClock: MainTestClock
diff --git a/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/DesktopInputDispatcher.kt b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/DesktopInputDispatcher.kt
index 4aaddd3..ce7e151 100644
--- a/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/DesktopInputDispatcher.kt
+++ b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/DesktopInputDispatcher.kt
@@ -19,18 +19,18 @@
 import androidx.compose.ui.input.pointer.PointerId
 import androidx.compose.ui.input.pointer.TestPointerInputEventData
 import androidx.compose.ui.node.RootForTest
-import androidx.compose.ui.platform.DesktopOwner
+import androidx.compose.ui.platform.DesktopRootForTest
 
 internal actual fun createInputDispatcher(
     testContext: TestContext,
     root: RootForTest
 ): InputDispatcher {
-    return DesktopInputDispatcher(testContext, root as DesktopOwner)
+    return DesktopInputDispatcher(testContext, root as DesktopRootForTest)
 }
 
 internal class DesktopInputDispatcher(
     testContext: TestContext,
-    val root: DesktopOwner
+    val root: DesktopRootForTest
 ) : InputDispatcher(testContext, root) {
     companion object {
         var gesturePointerId = 0L
diff --git a/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/TestComposeWindow.kt b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/TestComposeWindow.kt
deleted file mode 100644
index 1f05602..0000000
--- a/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/TestComposeWindow.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.test
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Providers
-import androidx.compose.ui.platform.DesktopOwner
-import androidx.compose.ui.platform.DesktopOwners
-import androidx.compose.ui.platform.DesktopPlatform
-import androidx.compose.ui.platform.DesktopPlatformAmbient
-import androidx.compose.ui.platform.setContent
-import androidx.compose.ui.unit.Density
-import org.jetbrains.skija.Surface
-
-class TestComposeWindow(
-    val width: Int,
-    val height: Int,
-    val density: Density = Density(1f, 1f),
-    var desktopPlatform: DesktopPlatform = DesktopPlatform.Linux
-) {
-    val surface = Surface.makeRasterN32Premul(width, height)!!
-    val canvas = surface.canvas
-    val owners = DesktopOwners(invalidate = {})
-
-    fun setContent(content: @Composable () -> Unit): DesktopOwners {
-        val owner = DesktopOwner(owners, density)
-        owner.setContent {
-            Providers(
-                DesktopPlatformAmbient provides desktopPlatform
-            ) {
-                content()
-            }
-        }
-        owner.setSize(width, height)
-        owner.measureAndLayout()
-        owner.draw(canvas)
-        return owners
-    }
-}
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopFont.kt b/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopFont.kt
index cfdbc6b..d6fda08 100644
--- a/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopFont.kt
+++ b/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopFont.kt
@@ -25,13 +25,13 @@
 import androidx.compose.ui.util.fastForEach
 import org.jetbrains.skija.Data
 import org.jetbrains.skija.FontMgr
-import org.jetbrains.skija.Typeface
+import org.jetbrains.skija.Typeface as SkTypeface
 import org.jetbrains.skija.paragraph.FontCollection
 import org.jetbrains.skija.paragraph.TypefaceFontProvider
 import java.io.File
 import java.security.MessageDigest
-import androidx.compose.ui.text.font.Font as ComposeFont
-import androidx.compose.ui.text.font.Typeface as ComposeTypeface
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.Typeface
 
 internal val GenericFontFamiliesMapping by lazy {
     when (Platform.Current) {
@@ -70,7 +70,7 @@
     }
 }
 
-sealed class DesktopFont : ComposeFont {
+sealed class DesktopFont : Font {
     abstract val identity: String
 
     internal val cacheKey: String
@@ -89,12 +89,38 @@
  *
  * @see FontFamily
  */
-data class LoadedFont(
+class LoadedFont internal constructor(
     override val identity: String,
     val data: ByteArray,
     override val weight: FontWeight,
     override val style: FontStyle
-) : DesktopFont()
+) : DesktopFont() {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as LoadedFont
+
+        if (identity != other.identity) return false
+        if (!data.contentEquals(other.data)) return false
+        if (weight != other.weight) return false
+        if (style != other.style) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = identity.hashCode()
+        result = 31 * result + data.contentHashCode()
+        result = 31 * result + weight.hashCode()
+        result = 31 * result + style.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "LoadedFont(identity='$identity', weight=$weight, style=$style)"
+    }
+}
 
 /**
  * Creates a Font using byte array with loaded font data.
@@ -113,7 +139,7 @@
     data: ByteArray,
     weight: FontWeight = FontWeight.Normal,
     style: FontStyle = FontStyle.Normal
-): DesktopFont = LoadedFont(identity, data, weight, style)
+): Font = LoadedFont(identity, data, weight, style)
 
 /**
  * Defines a Font using file path.
@@ -126,13 +152,37 @@
  *
  * @see FontFamily
  */
-data class FileFont(
+class FileFont internal constructor(
     val file: File,
     override val weight: FontWeight = FontWeight.Normal,
     override val style: FontStyle = FontStyle.Normal
 ) : DesktopFont() {
     override val identity
         get() = file.toString()
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as FileFont
+
+        if (file != other.file) return false
+        if (weight != other.weight) return false
+        if (style != other.style) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = file.hashCode()
+        result = 31 * result + weight.hashCode()
+        result = 31 * result + style.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "FileFont(file=$file, weight=$weight, style=$style)"
+    }
 }
 
 /**
@@ -150,7 +200,7 @@
     file: File,
     weight: FontWeight = FontWeight.Normal,
     style: FontStyle = FontStyle.Normal
-): DesktopFont = FileFont(file, weight, style)
+): Font = FileFont(file, weight, style)
 
 /**
  * Defines a Font using resource name.
@@ -164,13 +214,37 @@
  * @see FontFamily
  */
 
-data class ResourceFont(
+class ResourceFont internal constructor(
     val name: String,
     override val weight: FontWeight = FontWeight.Normal,
     override val style: FontStyle = FontStyle.Normal
 ) : DesktopFont() {
     override val identity
         get() = name
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as ResourceFont
+
+        if (name != other.name) return false
+        if (weight != other.weight) return false
+        if (style != other.style) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = name.hashCode()
+        result = 31 * result + weight.hashCode()
+        result = 31 * result + style.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "ResourceFont(name='$name', weight=$weight, style=$style)"
+    }
 }
 
 /**
@@ -188,21 +262,21 @@
     resource: String,
     weight: FontWeight = FontWeight.Normal,
     style: FontStyle = FontStyle.Normal
-): DesktopFont = ResourceFont(resource, weight, style)
+): Font = ResourceFont(resource, weight, style)
 
 internal class DesktopTypeface(
     val alias: String?,
-    val nativeTypeface: Typeface
-) : ComposeTypeface {
+    val nativeTypeface: SkTypeface
+) : Typeface {
     override val fontFamily: FontFamily? = null
 }
 
 /**
- * Returns a Compose [ComposeTypeface] from Skija [Typeface].
+ * Returns a Compose [Typeface] from Skija [SkTypeface].
  *
  * @param typeface Android Typeface instance
  */
-fun Typeface(typeface: Typeface, alias: String? = null): ComposeTypeface {
+fun Typeface(typeface: SkTypeface, alias: String? = null): Typeface {
     return DesktopTypeface(alias, typeface)
 }
 
@@ -220,7 +294,7 @@
 
 private fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
 
-class FontLoader : ComposeFont.ResourceLoader {
+class FontLoader : Font.ResourceLoader {
     val fonts = FontCollection()
     private val fontProvider = TypefaceFontProvider()
 
@@ -267,15 +341,15 @@
     //  proper interfaces or they are broken (.makeFromFile(*, 1) always fails)
     //  2. variable fonts. for them we also need to extend definition interfaces to support
     //  custom variation settings
-    override fun load(font: ComposeFont): Typeface {
+    override fun load(font: Font): SkTypeface {
         if (font !is DesktopFont) {
             throw IllegalArgumentException("Unsupported font type: $font")
         }
         return typefacesCache.get(font.cacheKey) {
             when (font) {
                 is ResourceFont -> typefaceResource(font.name)
-                is FileFont -> Typeface.makeFromFile(font.file.toString())
-                is LoadedFont -> Typeface.makeFromData(Data.makeFromBytes(font.data))
+                is FileFont -> SkTypeface.makeFromFile(font.file.toString())
+                is LoadedFont -> SkTypeface.makeFromData(Data.makeFromBytes(font.data))
             }
         }
     }
@@ -284,7 +358,7 @@
         fontFamily: FontFamily,
         fontWeight: FontWeight = FontWeight.Normal,
         fontStyle: FontStyle = FontStyle.Normal
-    ): Typeface? {
+    ): SkTypeface? {
         return when (fontFamily) {
             FontFamily.Default -> fonts.defaultFallback()
             else -> {
@@ -296,17 +370,17 @@
     }
 }
 
-private val typefacesCache = ExpireAfterAccessCache<String, Typeface>(
+private val typefacesCache = ExpireAfterAccessCache<String, SkTypeface>(
     60_000_000_000 // 1 minute
 )
 
-private fun typefaceResource(resourceName: String): Typeface {
+private fun typefaceResource(resourceName: String): SkTypeface {
     val resource = Thread
         .currentThread()
         .contextClassLoader
         .getResourceAsStream(resourceName) ?: error("Can't load font from $resourceName")
     val bytes = resource.readAllBytes()
-    return Typeface.makeFromData(Data.makeFromBytes(bytes))
+    return SkTypeface.makeFromData(Data.makeFromBytes(bytes))
 }
 
 private enum class Platform {
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt
index b4a14e6..aec5e08 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt
@@ -124,7 +124,7 @@
                 }
             }.flatten().groupBy { it.location }
 
-            Assert.assertTrue(boundingBoxes.size >= 6)
+            Assert.assertTrue(boundingBoxes.size >= 4)
         }
     }
 
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/SimpleComposablePreview.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/SimpleComposablePreview.kt
index 227f0b5..3c95990 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/SimpleComposablePreview.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/SimpleComposablePreview.kt
@@ -19,7 +19,7 @@
 import androidx.compose.material.Surface
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.savedinstancestate.AmbientUiSavedStateRegistry
+import androidx.compose.runtime.saveable.AmbientSaveableStateRegistry
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.AmbientLifecycleOwner
 import androidx.compose.ui.tooling.preview.Preview
@@ -79,11 +79,11 @@
 
 @Preview
 @Composable
-private fun UiSavedStateRegistryPreview() {
-    if (AmbientUiSavedStateRegistry.current == null) throw IllegalArgumentException(
-        "UiSavedStateRegistry is not provided"
+private fun SaveableStateRegistryPreview() {
+    if (AmbientSaveableStateRegistry.current == null) throw IllegalArgumentException(
+        "SaveableStateRegistry is not provided"
     )
-    Text("UiSavedStateRegistry preview")
+    Text("SaveableStateRegistry preview")
 }
 
 class TestGroup {
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt
index e096248..1bdff85 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt
@@ -239,7 +239,9 @@
 
     @Test
     fun testComposableLambda() = runBlocking {
-        val c: @Composable () -> Unit = { Text(text = "Hello World") }
+        // capture here to force the lambda to not be created as a singleton.
+        val capture = "Hello World"
+        val c: @Composable () -> Unit = { Text(text = capture) }
         val result = lookup(c as Any) ?: error("Lookup of ComposableLambda failed")
         val array = result.second as Array<*>
         assertThat(result.first).isEqualTo(ParameterType.Lambda)
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/ComposeViewAdapterTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/ComposeViewAdapterTest.kt
index 30b9537..05bbd6a 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/ComposeViewAdapterTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/ComposeViewAdapterTest.kt
@@ -238,10 +238,10 @@
     }
 
     @Test
-    fun uiSavedStateRegistryUsedInsidePreview() {
+    fun saveableStateRegistryUsedInsidePreview() {
         assertRendersCorrectly(
             "androidx.compose.ui.tooling.SimpleComposablePreviewKt",
-            "UiSavedStateRegistryPreview"
+            "SaveableStateRegistryPreview"
         )
     }
 
diff --git a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt
index 23d854c..92237c6 100644
--- a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt
+++ b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt
@@ -33,7 +33,6 @@
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.currentComposer
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.ui.graphics.Color
@@ -60,6 +59,8 @@
 
 const val TOOLS_NS_URI = "http://schemas.android.com/tools"
 
+private val emptyContent: @Composable () -> Unit = @Composable {}
+
 /**
  * Class containing the minimum information needed by the Preview to map components to the
  * source code and render boundaries.
@@ -146,14 +147,14 @@
      * The [Composable] to be rendered in the preview. It is initialized when this adapter
      * is initialized.
      */
-    private var previewComposition: @Composable () -> Unit = emptyContent()
+    private var previewComposition: @Composable () -> Unit = {}
 
-    // Note: the call to emptyContent() below instead of a literal {} works around
+    // Note: the constant emptyContent below instead of a literal {} works around
     // https://youtrack.jetbrains.com/issue/KT-17467, which causes the compiler to emit classes
     // named `content` and `Content` (from the Content method's composable update scope)
     // which causes compilation problems on case-insensitive filesystems.
     @Suppress("RemoveExplicitTypeArguments")
-    private val content = mutableStateOf<@Composable () -> Unit>(emptyContent())
+    private val content = mutableStateOf<@Composable () -> Unit>(emptyContent)
 
     /**
      * When true, the composition will be immediately invalidated after being drawn. This will
@@ -334,7 +335,7 @@
 
     private fun invalidateComposition() {
         // Invalidate the full composition by setting it to empty and back to the actual value
-        content.value = emptyContent()
+        content.value = {}
         content.value = previewComposition
         // Invalidate the state of the view so it gets redrawn
         invalidate()
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 05ed0ae..7e97814 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -2183,7 +2183,7 @@
   public final class DebugUtilsKt {
   }
 
-  public final class DisposableUiSavedStateRegistryKt {
+  public final class DisposableSaveableStateRegistryKt {
   }
 
   public interface InspectableValue {
@@ -2552,6 +2552,12 @@
     property public final float current;
     property public final kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> range;
     property public final int steps;
+    field public static final androidx.compose.ui.semantics.ProgressBarRangeInfo.Companion Companion;
+  }
+
+  public static final class ProgressBarRangeInfo.Companion {
+    method public androidx.compose.ui.semantics.ProgressBarRangeInfo getIndeterminate();
+    property public final androidx.compose.ui.semantics.ProgressBarRangeInfo Indeterminate;
   }
 
   public enum Role {
@@ -2696,6 +2702,7 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsDialog();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsPopup();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getPaneTitle();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getPassword();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ProgressBarRangeInfo> getProgressBarRangeInfo();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.Role> getRole();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getSelected();
@@ -2715,6 +2722,7 @@
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsDialog;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsPopup;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> PaneTitle;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Password;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ProgressBarRangeInfo> ProgressBarRangeInfo;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.Role> Role;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> Selected;
@@ -2753,6 +2761,7 @@
     method @androidx.compose.ui.ExperimentalComposeUiApi public static void invisibleToUser(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void onClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public static void onLongClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
+    method public static void password(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void pasteText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public static void popup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean> action);
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 05ed0ae..7e97814 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -2183,7 +2183,7 @@
   public final class DebugUtilsKt {
   }
 
-  public final class DisposableUiSavedStateRegistryKt {
+  public final class DisposableSaveableStateRegistryKt {
   }
 
   public interface InspectableValue {
@@ -2552,6 +2552,12 @@
     property public final float current;
     property public final kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> range;
     property public final int steps;
+    field public static final androidx.compose.ui.semantics.ProgressBarRangeInfo.Companion Companion;
+  }
+
+  public static final class ProgressBarRangeInfo.Companion {
+    method public androidx.compose.ui.semantics.ProgressBarRangeInfo getIndeterminate();
+    property public final androidx.compose.ui.semantics.ProgressBarRangeInfo Indeterminate;
   }
 
   public enum Role {
@@ -2696,6 +2702,7 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsDialog();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsPopup();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getPaneTitle();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getPassword();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ProgressBarRangeInfo> getProgressBarRangeInfo();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.Role> getRole();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getSelected();
@@ -2715,6 +2722,7 @@
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsDialog;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsPopup;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> PaneTitle;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Password;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ProgressBarRangeInfo> ProgressBarRangeInfo;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.Role> Role;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> Selected;
@@ -2753,6 +2761,7 @@
     method @androidx.compose.ui.ExperimentalComposeUiApi public static void invisibleToUser(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void onClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public static void onLongClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
+    method public static void password(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void pasteText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public static void popup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean> action);
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index cf8cdc8..9ccdb01 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -2245,7 +2245,7 @@
   public final class DebugUtilsKt {
   }
 
-  public final class DisposableUiSavedStateRegistryKt {
+  public final class DisposableSaveableStateRegistryKt {
   }
 
   public interface InspectableValue {
@@ -2616,6 +2616,12 @@
     property public final float current;
     property public final kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> range;
     property public final int steps;
+    field public static final androidx.compose.ui.semantics.ProgressBarRangeInfo.Companion Companion;
+  }
+
+  public static final class ProgressBarRangeInfo.Companion {
+    method public androidx.compose.ui.semantics.ProgressBarRangeInfo getIndeterminate();
+    property public final androidx.compose.ui.semantics.ProgressBarRangeInfo Indeterminate;
   }
 
   public enum Role {
@@ -2760,6 +2766,7 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsDialog();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsPopup();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getPaneTitle();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getPassword();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ProgressBarRangeInfo> getProgressBarRangeInfo();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.Role> getRole();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getSelected();
@@ -2779,6 +2786,7 @@
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsDialog;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsPopup;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> PaneTitle;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Password;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ProgressBarRangeInfo> ProgressBarRangeInfo;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.Role> Role;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> Selected;
@@ -2817,6 +2825,7 @@
     method @androidx.compose.ui.ExperimentalComposeUiApi public static void invisibleToUser(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void onClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public static void onLongClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
+    method public static void password(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void pasteText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
     method public static void popup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean> action);
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt
index f0834bc..127a50b 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt
@@ -26,7 +26,6 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
@@ -62,7 +61,7 @@
 @Composable
 private fun PressableContainer(
     modifier: Modifier = Modifier,
-    content: @Composable () -> Unit = emptyContent()
+    content: @Composable () -> Unit = {}
 ) {
     val defaultColor = DefaultBackgroundColor
     val pressedColor = PressedColor
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt
index 29765cf..e63b42f 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt
@@ -25,7 +25,6 @@
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
@@ -65,7 +64,7 @@
     orientation: Orientation,
     activeColor: Color,
     idleColor: Color,
-    content: @Composable () -> Unit = emptyContent()
+    content: @Composable () -> Unit = {}
 ) {
 
     val color = remember { mutableStateOf(idleColor) }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index 8c967c0..8fbadb1 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
@@ -62,6 +62,7 @@
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.unit.dp
 import androidx.core.view.ViewCompat
@@ -201,7 +202,8 @@
                                 .testTag(TextFieldTag),
                             value = value,
                             onValueChange = { value = it },
-                            onTextLayout = { textLayoutResult = it }
+                            onTextLayout = { textLayoutResult = it },
+                            visualTransformation = PasswordVisualTransformation()
                         )
                     }
                 }
@@ -788,6 +790,19 @@
         }
     }
 
+    @Test
+    fun testEventForPasswordTextField() {
+        val textFieldNode = rule.onNodeWithTag(TextFieldTag)
+            .fetchSemanticsNode("Couldn't fetch node with tag $TextFieldTag")
+
+        val event = delegate.createEvent(
+            textFieldNode.id,
+            AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
+        )
+
+        assertTrue(event.isPassword)
+    }
+
     private fun eventIndex(list: List<AccessibilityEvent>, event: AccessibilityEvent): Int {
         for (i in list.indices) {
             if (ReflectionEquals(list[i], null).matches(event)) {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
index 1a02253..4eca29f 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
@@ -41,7 +41,6 @@
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -534,7 +533,7 @@
                             assertEquals(childConstraints[0], constraints)
                             layout(0, 0) {}
                         },
-                        content = emptyContent(), modifier = Modifier.layoutId("header")
+                        content = {}, modifier = Modifier.layoutId("header")
                     )
                 }
                 val footer = @Composable {
@@ -543,14 +542,14 @@
                             assertEquals(childConstraints[1], constraints)
                             layout(0, 0) {}
                         },
-                        content = emptyContent(), modifier = Modifier.layoutId("footer")
+                        content = {}, modifier = Modifier.layoutId("footer")
                     )
                     Layout(
                         measureBlock = { _, constraints ->
                             assertEquals(childConstraints[2], constraints)
                             layout(0, 0) {}
                         },
-                        content = emptyContent(), modifier = Modifier.layoutId("footer")
+                        content = {}, modifier = Modifier.layoutId("footer")
                     )
                 }
 
@@ -1812,7 +1811,7 @@
             activity.setContent {
                 Layout(
                     content = {
-                        Layout(content = emptyContent()) { _, _ ->
+                        Layout(content = {}) { _, _ ->
                             latch.countDown()
                             layout(model.size, model.size) {}
                         }
@@ -1844,7 +1843,7 @@
                     content = {
                         Layout(
                             modifier = Modifier.graphicsLayer(),
-                            content = emptyContent()
+                            content = {}
                         ) { _, _ ->
                             latch.countDown()
                             layout(model.size, model.size) {}
@@ -2379,7 +2378,7 @@
                             }
                             layout(100, 100) {}
                         }
-                        FixedSize(30, content = emptyContent())
+                        FixedSize(30, content = {})
                     }
                 ) { measurables, constraints ->
                     val (first, second) = measurables
@@ -3599,7 +3598,7 @@
 fun AtLeastSize(
     size: Int,
     modifier: Modifier = Modifier,
-    content: @Composable () -> Unit = emptyContent()
+    content: @Composable () -> Unit = {}
 ) {
     Layout(
         measureBlock = { measurables, constraints ->
@@ -3641,7 +3640,7 @@
 fun FixedSize(
     size: Int,
     modifier: Modifier = Modifier,
-    content: @Composable () -> Unit = emptyContent()
+    content: @Composable () -> Unit = {}
 ) {
     Layout(content = content, modifier = modifier) { measurables, _ ->
         val newConstraints = Constraints.fixed(size, size)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/OpenComposeView.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/OpenComposeView.kt
index 9f9e5a6..ece2f94 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/OpenComposeView.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/OpenComposeView.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.util.AttributeSet
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.platform.AbstractComposeView
 
@@ -31,7 +30,7 @@
 ) : AbstractComposeView(context, attrs, defStyleAttr) {
 
     @Suppress("RemoveExplicitTypeArguments")
-    private val content = mutableStateOf<@Composable () -> Unit>(emptyContent())
+    private val content = mutableStateOf<@Composable () -> Unit>({})
 
     @Composable
     override fun Content() {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
index 20e1323..6eb6703 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
@@ -16,7 +16,6 @@
 package androidx.compose.ui
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.Layout
@@ -116,13 +115,13 @@
                 val header = @Composable {
                     Layout(
                         modifier = Modifier.layoutId(0),
-                        content = emptyContent()
+                        content = {}
                     ) { _, _ -> layout(0, 0) {} }
                 }
                 val footer = @Composable {
                     Layout(
                         modifier = Modifier.layoutId(1),
-                        content = emptyContent()
+                        content = {}
                     ) { _, _ -> layout(0, 0) {} }
                 }
 
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt
index 4cf87b1..7d21101 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt
@@ -22,7 +22,6 @@
 import android.view.ViewTreeObserver
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -1046,7 +1045,7 @@
 private fun FixedSize(
     size: State<Int>,
     modifier: Modifier = Modifier,
-    content: @Composable () -> Unit = emptyContent()
+    content: @Composable () -> Unit = {}
 ) {
     Layout(content = content, modifier = modifier) { measurables, _ ->
         val newConstraints = Constraints.fixed(size.value, size.value)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt
index 9e12c1b..11f062a 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt
@@ -19,7 +19,6 @@
 import android.view.View
 import android.view.ViewGroup
 import androidx.compose.foundation.layout.Box
-import androidx.compose.runtime.emptyContent
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.Layout
@@ -85,7 +84,7 @@
                                 setupLatch.countDown()
                             }
                         },
-                        content = emptyContent()
+                        content = {}
                     )
                 }
             }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
index 8a987ae..3ff7e74 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
@@ -23,7 +23,6 @@
 import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.ExperimentalComposeApi
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -591,7 +590,7 @@
 @Suppress("TestFunctionName")
 @Composable
 private fun FillLayout(modifier: Modifier = Modifier) {
-    Layout(emptyContent(), modifier) { _, constraints ->
+    Layout({}, modifier) { _, constraints ->
         layout(constraints.maxWidth, constraints.maxHeight) {}
     }
 }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/ClipPointerInputTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/ClipPointerInputTest.kt
index fa5a9be..3d6a2c6 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/ClipPointerInputTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/ClipPointerInputTest.kt
@@ -21,7 +21,6 @@
 import android.view.ViewGroup
 import androidx.compose.foundation.layout.offset
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clipToBounds
 import androidx.compose.ui.geometry.Offset
@@ -316,7 +315,7 @@
 
     @Composable
     fun Child(modifier: Modifier) {
-        Layout(content = emptyContent(), modifier = modifier) { _, _ ->
+        Layout(content = {}, modifier = modifier) { _, _ ->
             layout(2, 2) {}
         }
     }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt
index 2579115..d492eac 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt
@@ -23,7 +23,6 @@
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
@@ -151,7 +150,7 @@
 }
 
 @Composable
-fun SimpleLayout(modifier: Modifier, content: @Composable () -> Unit = emptyContent()) {
+fun SimpleLayout(modifier: Modifier, content: @Composable () -> Unit = {}) {
     Layout(
         content,
         modifier
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
index bac20cd..8d0b574 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
@@ -26,7 +26,6 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -661,7 +660,7 @@
 fun DelayedMeasure(
     size: Int,
     modifier: Modifier = Modifier,
-    content: @Composable () -> Unit = emptyContent()
+    content: @Composable () -> Unit = {}
 ) {
     Layout(content = content, modifier = modifier) { measurables, _ ->
         layout(size, size) {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
index 06f92fe..7ea1393 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
@@ -22,7 +22,6 @@
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.FixedSize
 import androidx.compose.ui.Modifier
@@ -261,7 +260,7 @@
                 Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
                     Box {
                         Providers(AmbientLayoutDirection provides initialLayoutDirection) {
-                            Layout(emptyContent()) { _, _ ->
+                            Layout({}) { _, _ ->
                                 resultLayoutDirection.value = layoutDirection
                                 latch.countDown()
                                 layout(0, 0) {}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/UiSavedStateRegistryTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/DisposableSaveableStateRegistryTest.kt
similarity index 88%
rename from compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/UiSavedStateRegistryTest.kt
rename to compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/DisposableSaveableStateRegistryTest.kt
index b93ac1d..0124586 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/UiSavedStateRegistryTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/DisposableSaveableStateRegistryTest.kt
@@ -41,7 +41,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class DisposableUiSavedStateRegistryTest {
+class DisposableSaveableStateRegistryTest {
 
     private val ContainerKey = 100
     private val SaveKey = "key"
@@ -51,12 +51,12 @@
     @Test
     fun simpleSaveAndRestore() {
         val owner1 = TestOwner()
-        var registry = DisposableUiSavedStateRegistry(ContainerKey, owner1)
+        var registry = DisposableSaveableStateRegistry(ContainerKey, owner1)
         registry.registerProvider(SaveKey) { SaveValue }
         val bundle = owner1.save()
 
         val owner2 = TestOwner(bundle)
-        registry = DisposableUiSavedStateRegistry(ContainerKey, owner2)
+        registry = DisposableSaveableStateRegistry(ContainerKey, owner2)
         val restoredValue = registry.consumeRestored(SaveKey)
         assertEquals(SaveValue, restoredValue)
     }
@@ -72,22 +72,22 @@
         val value2 = 2
 
         // save first view
-        val registryToSave1 = DisposableUiSavedStateRegistry(parentKey1, owner1)
+        val registryToSave1 = DisposableSaveableStateRegistry(parentKey1, owner1)
         registryToSave1.registerProvider(SaveKey) { value1 }
 
         // save second view
-        val registryToSave2 = DisposableUiSavedStateRegistry(parentKey2, owner1)
+        val registryToSave2 = DisposableSaveableStateRegistry(parentKey2, owner1)
         registryToSave2.registerProvider(SaveKey) { value2 }
 
         val owner2 = TestOwner(owner1.save())
 
         // restore first view
-        val registryToRestore1 = DisposableUiSavedStateRegistry(parentKey1, owner2)
+        val registryToRestore1 = DisposableSaveableStateRegistry(parentKey1, owner2)
         val restoredValue1 = registryToRestore1.consumeRestored(SaveKey)
         assertEquals(value1, restoredValue1)
 
         // restore second view
-        val registryToRestore2 = DisposableUiSavedStateRegistry(parentKey2, owner2)
+        val registryToRestore2 = DisposableSaveableStateRegistry(parentKey2, owner2)
         val restoredValue2 = registryToRestore2.consumeRestored(SaveKey)
         assertEquals(value2, restoredValue2)
     }
@@ -95,7 +95,7 @@
     @UiThreadTest
     @Test
     fun typesSupportedByBaseBundleCanBeSaved() {
-        val registry = DisposableUiSavedStateRegistry(ContainerKey, TestOwner())
+        val registry = DisposableSaveableStateRegistry(ContainerKey, TestOwner())
 
         assertTrue(registry.canBeSaved(true))
         assertTrue(registry.canBeSaved(true.asBoxed()))
@@ -114,7 +114,7 @@
     @UiThreadTest
     @Test
     fun typesSupportedByBundleCanBeSaved() {
-        val registry = DisposableUiSavedStateRegistry(ContainerKey, TestOwner())
+        val registry = DisposableSaveableStateRegistry(ContainerKey, TestOwner())
 
         assertTrue(registry.canBeSaved(Binder()))
         assertTrue(registry.canBeSaved(Bundle()))
@@ -148,7 +148,7 @@
     @UiThreadTest
     @Test
     fun customTypeCantBeSaved() {
-        val registry = DisposableUiSavedStateRegistry(ContainerKey, TestOwner())
+        val registry = DisposableSaveableStateRegistry(ContainerKey, TestOwner())
 
         assertFalse(registry.canBeSaved(CustomClass()))
     }
@@ -156,7 +156,7 @@
     @UiThreadTest
     @Test
     fun charSequenceCantBeSaved() {
-        val registry = DisposableUiSavedStateRegistry(ContainerKey, TestOwner())
+        val registry = DisposableSaveableStateRegistry(ContainerKey, TestOwner())
 
         assertFalse(registry.canBeSaved(CustomCharSequence()))
     }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/LayoutIdTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/LayoutIdTest.kt
index 459bc94..f1311c7 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/LayoutIdTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/LayoutIdTest.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.platform
 
 import androidx.compose.foundation.layout.Box
-import androidx.compose.runtime.emptyContent
 import androidx.compose.ui.AtLeastSize
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
@@ -63,15 +62,15 @@
             activity.setContent {
                 Layout(
                     {
-                        AtLeastSize(0, Modifier.layoutId("first"), content = emptyContent())
+                        AtLeastSize(0, Modifier.layoutId("first"), content = {})
                         Box(Modifier.layoutId("second")) {
                             AtLeastSize(
                                 0,
-                                content = emptyContent()
+                                content = {}
                             )
                         }
                         Box(Modifier.layoutId("third")) {
-                            AtLeastSize(0, content = emptyContent())
+                            AtLeastSize(0, content = {})
                         }
                     }
                 ) { measurables, _ ->
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
index 0a89378..0e2c070 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
@@ -21,7 +21,6 @@
 import androidx.activity.ComponentActivity
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -116,7 +115,7 @@
                 onDismissRequest = { },
                 properties = dialogProperties
             ) {
-                SimpleContainer(Modifier.preferredSize(50.dp), content = emptyContent())
+                SimpleContainer(Modifier.preferredSize(50.dp), content = {})
             }
         }
     }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupAlignmentTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupAlignmentTest.kt
index 755f8fe..a0de7cc 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupAlignmentTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupAlignmentTest.kt
@@ -19,7 +19,6 @@
 import android.view.View
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
-import androidx.compose.runtime.emptyContent
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
@@ -332,7 +331,7 @@
                                         modifier = Modifier.onGloballyPositioned {
                                             measureLatch.countDown()
                                         },
-                                        content = emptyContent()
+                                        content = {}
                                     )
                                 }
                             }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupSecureFlagTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupSecureFlagTest.kt
index 8e4fdd6..95d7e63 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupSecureFlagTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupSecureFlagTest.kt
@@ -20,7 +20,6 @@
 import androidx.activity.ComponentActivity
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -123,7 +122,7 @@
                     alignment = Alignment.Center,
                     properties = popupProperties
                 ) {
-                    SimpleContainer(Modifier.preferredSize(50.dp), content = emptyContent())
+                    SimpleContainer(Modifier.preferredSize(50.dp), content = {})
                 }
             }
         }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupTest.kt
index 7568a99..207aed8 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupTest.kt
@@ -23,7 +23,6 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.ambientOf
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -81,7 +80,7 @@
             SimpleContainer {
                 PopupTestTag(testTag) {
                     Popup(alignment = Alignment.Center) {
-                        SimpleContainer(Modifier.preferredSize(50.dp), content = emptyContent())
+                        SimpleContainer(Modifier.preferredSize(50.dp), content = {})
                     }
                 }
             }
@@ -106,7 +105,7 @@
                         SimpleContainer(
                             width = popupWidthDp,
                             height = popupHeightDp,
-                            content = emptyContent()
+                            content = {}
                         )
                     }
                 }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt
index 7209338..88b2a04 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAmbients.kt
@@ -31,7 +31,7 @@
 import androidx.compose.runtime.neverEqualPolicy
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.savedinstancestate.AmbientUiSavedStateRegistry
+import androidx.compose.runtime.saveable.AmbientSaveableStateRegistry
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.staticAmbientOf
 import androidx.compose.ui.ExperimentalComposeUiApi
@@ -98,12 +98,12 @@
         "Called when the ViewTreeOwnersAvailability is not yet in Available state"
     )
 
-    val uiSavedStateRegistry = remember {
-        DisposableUiSavedStateRegistry(view, viewTreeOwners.savedStateRegistryOwner)
+    val saveableStateRegistry = remember {
+        DisposableSaveableStateRegistry(view, viewTreeOwners.savedStateRegistryOwner)
     }
     DisposableEffect(Unit) {
         onDispose {
-            uiSavedStateRegistry.dispose()
+            saveableStateRegistry.dispose()
         }
     }
 
@@ -112,7 +112,7 @@
         AmbientContext provides context,
         AmbientLifecycleOwner provides viewTreeOwners.lifecycleOwner,
         AmbientSavedStateRegistryOwner provides viewTreeOwners.savedStateRegistryOwner,
-        AmbientUiSavedStateRegistry provides uiSavedStateRegistry,
+        AmbientSaveableStateRegistry provides saveableStateRegistry,
         AmbientView provides owner.view,
         AmbientViewModelStoreOwner provides viewTreeOwners.viewModelStoreOwner
     ) {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.kt
index adff70b..ea17709 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.kt
@@ -26,7 +26,6 @@
 import android.util.Log
 import android.view.MotionEvent
 import android.view.View
-import android.view.ViewParent
 import android.view.accessibility.AccessibilityEvent
 import android.view.accessibility.AccessibilityManager
 import android.view.accessibility.AccessibilityNodeInfo
@@ -58,6 +57,7 @@
 import androidx.compose.ui.text.platform.toAccessibilitySpannableString
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.semantics.ProgressBarRangeInfo
 import androidx.core.view.AccessibilityDelegateCompat
 import androidx.core.view.ViewCompat
 import androidx.core.view.accessibility.AccessibilityEventCompat
@@ -311,6 +311,7 @@
         semanticsNode.config.getOrNull(SemanticsProperties.Heading)?.let {
             info.isHeading = true
         }
+        info.isPassword = semanticsNode.isPassword
         // Note editable is not added to semantics properties api.
         info.isEditable = semanticsNode.config.contains(SemanticsActions.SetText)
         info.isEnabled = semanticsNode.enabled()
@@ -366,14 +367,6 @@
                     )
                 )
             }
-            semanticsNode.config.getOrNull(SemanticsActions.SetSelection)?.let {
-                info.addAction(
-                    AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_SET_SELECTION,
-                        it.label
-                    )
-                )
-            }
 
             // The config will contain this action only if there is a text selection at the moment.
             semanticsNode.config.getOrNull(SemanticsActions.CutText)?.let {
@@ -405,12 +398,15 @@
                 getAccessibilitySelectionStart(semanticsNode),
                 getAccessibilitySelectionEnd(semanticsNode)
             )
-            if (semanticsNode.config.getOrNull(SemanticsActions.SetSelection) == null ||
-                !semanticsNode.enabled()
-            ) {
-                // Note this is the default set selection action provided by the framework.
-                info.addAction(AccessibilityNodeInfoCompat.ACTION_SET_SELECTION)
-            }
+            val setSelectionAction = semanticsNode.config.getOrNull(SemanticsActions.SetSelection)
+            // ACTION_SET_SELECTION should be provided even when SemanticsActions.SetSelection
+            // semantics action is not provided by the component
+            info.addAction(
+                AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                    AccessibilityNodeInfoCompat.ACTION_SET_SELECTION,
+                    setSelectionAction?.label
+                )
+            )
             info.addAction(AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY)
             info.addAction(AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY)
             info.movementGranularities =
@@ -440,12 +436,14 @@
             } else {
                 info.className = "android.widget.ProgressBar"
             }
-            info.rangeInfo = AccessibilityNodeInfoCompat.RangeInfoCompat.obtain(
-                AccessibilityNodeInfoCompat.RangeInfoCompat.RANGE_TYPE_FLOAT,
-                rangeInfo.range.start,
-                rangeInfo.range.endInclusive,
-                rangeInfo.current
-            )
+            if (rangeInfo !== ProgressBarRangeInfo.Indeterminate) {
+                info.rangeInfo = AccessibilityNodeInfoCompat.RangeInfoCompat.obtain(
+                    AccessibilityNodeInfoCompat.RangeInfoCompat.RANGE_TYPE_FLOAT,
+                    rangeInfo.range.start,
+                    rangeInfo.range.endInclusive,
+                    rangeInfo.current
+                )
+            }
             if (semanticsNode.config.contains(SemanticsActions.SetProgress) &&
                 semanticsNode.enabled()
             ) {
@@ -649,9 +647,7 @@
             if (focusedVirtualViewId != InvalidId) {
                 sendEventForVirtualView(
                     focusedVirtualViewId,
-                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
-                    null,
-                    null
+                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
                 )
             }
 
@@ -661,9 +657,7 @@
             view.invalidate()
             sendEventForVirtualView(
                 virtualViewId,
-                AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
-                null,
-                null
+                AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
             )
             return true
         }
@@ -699,8 +693,6 @@
             return false
         }
 
-        val parent: ViewParent = view.parent
-
         val event: AccessibilityEvent = createEvent(virtualViewId, eventType)
         if (contentChangeType != null) {
             event.contentChangeTypes = contentChangeType
@@ -709,7 +701,7 @@
             event.contentDescription = contentDescription
         }
 
-        return parent.requestSendAccessibilityEvent(view, event)
+        return sendEvent(event)
     }
 
     /**
@@ -746,6 +738,11 @@
         event.packageName = view.context.packageName
         event.setSource(view, virtualViewId)
 
+        // populate additional information from the node
+        currentSemanticsNodes[virtualViewId]?.let {
+            event.isPassword = it.isPassword
+        }
+
         return event
     }
 
@@ -762,9 +759,7 @@
             view.invalidate()
             sendEventForVirtualView(
                 virtualViewId,
-                AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
-                null,
-                null
+                AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
             )
             return true
         }
@@ -1199,18 +1194,8 @@
         Stay consistent with framework behavior by sending ENTER/EXIT pairs
         in reverse order. This is accurate as of API 18.
         */
-        sendEventForVirtualView(
-            virtualViewId,
-            AccessibilityEvent.TYPE_VIEW_HOVER_ENTER,
-            null,
-            null
-        )
-        sendEventForVirtualView(
-            previousVirtualViewId,
-            AccessibilityEvent.TYPE_VIEW_HOVER_EXIT,
-            null,
-            null
-        )
+        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
+        sendEventForVirtualView(previousVirtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT)
     }
 
     override fun getAccessibilityNodeProvider(host: View?): AccessibilityNodeProviderCompat {
@@ -1342,11 +1327,10 @@
             return
         }
 
-        sendEvent(
-            createEvent(
-                semanticsNodeIdToAccessibilityVirtualNodeId(id),
-                AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
-            ).also { it.contentChangeTypes = AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE }
+        sendEventForVirtualView(
+            semanticsNodeIdToAccessibilityVirtualNodeId(id),
+            AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
+            AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
         )
     }
 
@@ -1479,8 +1463,7 @@
                             sendEventForVirtualView(
                                 semanticsNodeIdToAccessibilityVirtualNodeId(id),
                                 AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
-                                AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT,
-                                null
+                                AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT
                             )
                         }
                     }
@@ -1914,6 +1897,7 @@
 }
 
 private fun SemanticsNode.hasPaneTitle() = config.contains(SemanticsProperties.PaneTitle)
+private val SemanticsNode.isPassword: Boolean get() = config.contains(SemanticsProperties.Password)
 
 /**
  * Finds pruned [SemanticsNode]s in the tree owned by this [SemanticsOwner]. A semantics node
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/DisposableSaveableStateRegistry.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/DisposableSaveableStateRegistry.kt
new file mode 100644
index 0000000..0c60a2c
--- /dev/null
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/DisposableSaveableStateRegistry.kt
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+import android.annotation.SuppressLint
+import android.os.Binder
+import android.os.Bundle
+import android.os.Parcel
+import android.os.Parcelable
+import android.util.Size
+import android.util.SizeF
+import android.util.SparseArray
+import android.view.View
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.neverEqualPolicy
+import androidx.compose.runtime.referentialEqualityPolicy
+import androidx.compose.runtime.saveable.SaveableStateRegistry
+import androidx.compose.runtime.snapshots.SnapshotMutableState
+import androidx.compose.runtime.structuralEqualityPolicy
+import androidx.compose.ui.util.fastForEachIndexed
+import androidx.savedstate.SavedStateRegistry
+import androidx.savedstate.SavedStateRegistryOwner
+import java.io.Serializable
+
+/**
+ * Creates [DisposableSaveableStateRegistry] associated with these [view] and [owner].
+ */
+internal fun DisposableSaveableStateRegistry(
+    view: View,
+    owner: SavedStateRegistryOwner
+): DisposableSaveableStateRegistry {
+    // When AndroidComposeView is composed into some ViewGroup we just added as a child for this
+    // ViewGroup. And we don't have any id on AndroidComposeView as we can't make it unique, but
+    // we require this parent ViewGroup to have an unique id for the saved instance state mechanism
+    // to work (similarly to how it works without Compose). When we composed into Activity our
+    // parent is the ViewGroup with android.R.id.content.
+    val parentId: Int = (view.parent as? View)?.id ?: View.NO_ID
+    return DisposableSaveableStateRegistry(parentId, owner)
+}
+
+/**
+ * Creates [DisposableSaveableStateRegistry] with the restored values using [SavedStateRegistry] and
+ * saves the values when [SavedStateRegistry] performs save.
+ *
+ * To provide a namespace we require unique [id]. We can't use the default way of doing it when we
+ * have unique id on [AndroidComposeView] because we dynamically create [AndroidComposeView]s and
+ * there is no way to have a unique id given there are could be any number of
+ * [AndroidComposeView]s inside the same Activity. If we use [View.generateViewId]
+ * this id will not survive Activity recreation.
+ * But it is reasonable to ask our users to have an unique id on the parent ViewGroup in which we
+ * compose our [AndroidComposeView]. If Activity.setContent is used then it will be a View with
+ * [android.R.id.content], if ViewGroup.setContent is used then we will ask users to provide an
+ * id for this ViewGroup. If @GenerateView will be used then we will ask users to set an id on
+ * this generated View.
+ */
+internal fun DisposableSaveableStateRegistry(
+    id: Int,
+    savedStateRegistryOwner: SavedStateRegistryOwner
+): DisposableSaveableStateRegistry {
+    val key = "${SaveableStateRegistry::class.java.simpleName}:$id"
+
+    val androidxRegistry = savedStateRegistryOwner.savedStateRegistry
+    val bundle = androidxRegistry.consumeRestoredStateForKey(key)
+    val restored: Map<String, List<Any?>>? = bundle?.toMap()
+
+    val saveableStateRegistry = SaveableStateRegistry(restored) {
+        canBeSavedToBundle(it)
+    }
+    val registered = try {
+        androidxRegistry.registerSavedStateProvider(key) {
+            saveableStateRegistry.performSave().toBundle()
+        }
+        true
+    } catch (ignore: IllegalArgumentException) {
+        // this means there are two AndroidComposeViews composed into different parents with the
+        // same view id. currently we will just not save/restore state for the second
+        // AndroidComposeView.
+        // TODO: we should verify our strategy for such cases and improve it. b/162397322
+        false
+    }
+    return DisposableSaveableStateRegistry(saveableStateRegistry) {
+        if (registered) {
+            androidxRegistry.unregisterSavedStateProvider(key)
+        }
+    }
+}
+
+/**
+ * [SaveableStateRegistry] which can be disposed using [dispose].
+ */
+internal class DisposableSaveableStateRegistry(
+    saveableStateRegistry: SaveableStateRegistry,
+    private val onDispose: () -> Unit
+) : SaveableStateRegistry by saveableStateRegistry {
+
+    fun dispose() {
+        onDispose()
+    }
+}
+
+/**
+ * Checks that [value] can be stored inside [Bundle].
+ */
+private fun canBeSavedToBundle(value: Any): Boolean {
+    for (cl in AcceptableClasses) {
+        if (cl.isInstance(value)) {
+            return true
+        }
+    }
+    if (value is SnapshotMutableState<*>) {
+        if (value.policy === neverEqualPolicy<Any?>() ||
+            value.policy === structuralEqualityPolicy<Any?>() ||
+            value.policy === referentialEqualityPolicy<Any?>()
+        ) {
+            val stateValue = value.value
+            return if (stateValue == null) true else canBeSavedToBundle(stateValue)
+        }
+    }
+    return false
+}
+
+/**
+ * Contains Classes which can be stored inside [Bundle].
+ *
+ * Some of the classes are not added separately because:
+ *
+ * This classes implement Serializable:
+ * - Arrays (DoubleArray, BooleanArray, IntArray, LongArray, ByteArray, FloatArray, ShortArray,
+ * CharArray, Array<Parcelable, Array<String>)
+ * - ArrayList
+ * - Primitives (Boolean, Int, Long, Double, Float, Byte, Short, Char) will be boxed when casted
+ * to Any, and all the boxed classes implements Serializable.
+ * This class implements Parcelable:
+ * - Bundle
+ *
+ * Note: it is simplified copy of the array from SavedStateHandle (lifecycle-viewmodel-savedstate).
+ */
+private val AcceptableClasses = arrayOf(
+    Serializable::class.java,
+    Parcelable::class.java,
+    String::class.java,
+    SparseArray::class.java,
+    Binder::class.java,
+    Size::class.java,
+    SizeF::class.java
+)
+
+private fun Bundle.toMap(): Map<String, List<Any?>>? {
+    val map = mutableMapOf<String, List<Any?>>()
+    this.keySet().forEach { key ->
+        @Suppress("UNCHECKED_CAST")
+        val list = getParcelableArrayList<Parcelable?>(key) as ArrayList<Any?>
+        list.fastForEachIndexed { index, value ->
+            if (value is ParcelableMutableStateHolder) {
+                list[index] = value.state
+            }
+        }
+        map[key] = list
+    }
+    return map
+}
+
+private fun Map<String, List<Any?>>.toBundle(): Bundle {
+    val bundle = Bundle()
+    forEach { (key, list) ->
+        val arrayList = if (list is ArrayList<Any?>) list else ArrayList(list)
+        arrayList.fastForEachIndexed { index, value ->
+            if (value is SnapshotMutableState<*>) {
+                arrayList[index] = ParcelableMutableStateHolder(value)
+            }
+        }
+        @Suppress("UNCHECKED_CAST")
+        bundle.putParcelableArrayList(
+            key,
+            arrayList as ArrayList<Parcelable?>
+        )
+    }
+    return bundle
+}
+
+@SuppressLint("BanParcelableUsage")
+private class ParcelableMutableStateHolder : Parcelable {
+
+    val state: SnapshotMutableState<*>
+
+    constructor(state: SnapshotMutableState<*>) {
+        this.state = state
+    }
+
+    private constructor(parcel: Parcel, loader: ClassLoader?) {
+        val value = parcel.readValue(loader ?: javaClass.classLoader)
+        val policyIndex = parcel.readInt()
+        state = mutableStateOf(
+            value,
+            when (policyIndex) {
+                PolicyNeverEquals -> neverEqualPolicy()
+                PolicyStructuralEquality -> structuralEqualityPolicy()
+                PolicyReferentialEquality -> referentialEqualityPolicy()
+                else -> throw IllegalStateException(
+                    "Restored an incorrect MutableState policy $policyIndex"
+                )
+            }
+        ) as SnapshotMutableState
+    }
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.writeValue(state.value)
+        parcel.writeInt(
+            when (state.policy) {
+                neverEqualPolicy<Any?>() -> PolicyNeverEquals
+                structuralEqualityPolicy<Any?>() -> PolicyStructuralEquality
+                referentialEqualityPolicy<Any?>() -> PolicyReferentialEquality
+                else -> throw IllegalStateException(
+                    "Only known types of MutableState's SnapshotMutationPolicy are supported"
+                )
+            }
+        )
+    }
+
+    override fun describeContents(): Int {
+        return 0
+    }
+
+    companion object {
+        private const val PolicyNeverEquals = 0
+        private const val PolicyStructuralEquality = 1
+        private const val PolicyReferentialEquality = 2
+
+        @Suppress("unused")
+        @JvmField
+        val CREATOR: Parcelable.Creator<ParcelableMutableStateHolder> =
+            object : Parcelable.ClassLoaderCreator<ParcelableMutableStateHolder> {
+                override fun createFromParcel(parcel: Parcel, loader: ClassLoader) =
+                    ParcelableMutableStateHolder(parcel, loader)
+
+                override fun createFromParcel(parcel: Parcel) =
+                    ParcelableMutableStateHolder(parcel, null)
+
+                override fun newArray(size: Int) = arrayOfNulls<ParcelableMutableStateHolder?>(size)
+            }
+    }
+}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/DisposableUiSavedStateRegistry.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/DisposableUiSavedStateRegistry.kt
deleted file mode 100644
index e38953bf..0000000
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/DisposableUiSavedStateRegistry.kt
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.platform
-
-import android.os.Binder
-import android.os.Bundle
-import android.os.Parcelable
-import android.util.Size
-import android.util.SizeF
-import android.util.SparseArray
-import android.view.View
-import androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry
-import androidx.savedstate.SavedStateRegistry
-import androidx.savedstate.SavedStateRegistryOwner
-import java.io.Serializable
-
-/**
- * Creates [DisposableUiSavedStateRegistry] associated with these [view] and [owner].
- */
-internal fun DisposableUiSavedStateRegistry(
-    view: View,
-    owner: SavedStateRegistryOwner
-): DisposableUiSavedStateRegistry {
-    // When AndroidComposeView is composed into some ViewGroup we just added as a child for this
-    // ViewGroup. And we don't have any id on AndroidComposeView as we can't make it unique, but
-    // we require this parent ViewGroup to have an unique id for the saved instance state mechanism
-    // to work (similarly to how it works without Compose). When we composed into Activity our
-    // parent is the ViewGroup with android.R.id.content.
-    val parentId: Int = (view.parent as? View)?.id ?: View.NO_ID
-    return DisposableUiSavedStateRegistry(parentId, owner)
-}
-
-/**
- * Creates [DisposableUiSavedStateRegistry] with the restored values using [SavedStateRegistry] and
- * saves the values when [SavedStateRegistry] performs save.
- *
- * To provide a namespace we require unique [id]. We can't use the default way of doing it when we
- * have unique id on [AndroidComposeView] because we dynamically create [AndroidComposeView]s and
- * there is no way to have a unique id given there are could be any number of
- * [AndroidComposeView]s inside the same Activity. If we use [View.generateViewId]
- * this id will not survive Activity recreation.
- * But it is reasonable to ask our users to have an unique id on the parent ViewGroup in which we
- * compose our [AndroidComposeView]. If Activity.setContent is used then it will be a View with
- * [android.R.id.content], if ViewGroup.setContent is used then we will ask users to provide an
- * id for this ViewGroup. If @GenerateView will be used then we will ask users to set an id on
- * this generated View.
- */
-internal fun DisposableUiSavedStateRegistry(
-    id: Int,
-    savedStateRegistryOwner: SavedStateRegistryOwner
-): DisposableUiSavedStateRegistry {
-    val key = "${UiSavedStateRegistry::class.java.simpleName}:$id"
-
-    val androidxRegistry = savedStateRegistryOwner.savedStateRegistry
-    val bundle = androidxRegistry.consumeRestoredStateForKey(key)
-    val restored: Map<String, List<Any?>>? = bundle?.toMap()
-
-    val uiSavedStateRegistry = UiSavedStateRegistry(restored) {
-        canBeSavedToBundle(it)
-    }
-    val registered = try {
-        androidxRegistry.registerSavedStateProvider(key) {
-            uiSavedStateRegistry.performSave().toBundle()
-        }
-        true
-    } catch (ignore: IllegalArgumentException) {
-        // this means there are two AndroidComposeViews composed into different parents with the
-        // same view id. currently we will just not save/restore state for the second
-        // AndroidComposeView.
-        // TODO: we should verify our strategy for such cases and improve it. b/162397322
-        false
-    }
-    return DisposableUiSavedStateRegistry(uiSavedStateRegistry) {
-        if (registered) {
-            androidxRegistry.unregisterSavedStateProvider(key)
-        }
-    }
-}
-
-/**
- * [UiSavedStateRegistry] which can be disposed using [dispose].
- */
-internal class DisposableUiSavedStateRegistry(
-    uiSavedStateRegistry: UiSavedStateRegistry,
-    private val onDispose: () -> Unit
-) : UiSavedStateRegistry by uiSavedStateRegistry {
-
-    fun dispose() {
-        onDispose()
-    }
-}
-
-/**
- * Checks that [value] can be stored inside [Bundle].
- */
-private fun canBeSavedToBundle(value: Any): Boolean {
-    for (cl in AcceptableClasses) {
-        if (cl.isInstance(value)) {
-            return true
-        }
-    }
-    return false
-}
-
-/**
- * Contains Classes which can be stored inside [Bundle].
- *
- * Some of the classes are not added separately because:
- *
- * This classes implement Serializable:
- * - Arrays (DoubleArray, BooleanArray, IntArray, LongArray, ByteArray, FloatArray, ShortArray,
- * CharArray, Array<Parcelable, Array<String>)
- * - ArrayList
- * - Primitives (Boolean, Int, Long, Double, Float, Byte, Short, Char) will be boxed when casted
- * to Any, and all the boxed classes implements Serializable.
- * This class implements Parcelable:
- * - Bundle
- *
- * Note: it is simplified copy of the array from SavedStateHandle (lifecycle-viewmodel-savedstate).
- */
-private val AcceptableClasses = arrayOf(
-    Serializable::class.java,
-    Parcelable::class.java,
-    String::class.java,
-    SparseArray::class.java,
-    Binder::class.java,
-    Size::class.java,
-    SizeF::class.java
-)
-
-private fun Bundle.toMap(): Map<String, List<Any?>>? {
-    val map = mutableMapOf<String, List<Any?>>()
-    this.keySet().forEach { key ->
-        map[key] = getParcelableArrayList<Parcelable?>(key) as List<Any?>
-    }
-    return map
-}
-
-private fun Map<String, List<Any?>>.toBundle(): Bundle {
-    val bundle = Bundle()
-    forEach { (key, list) ->
-        val arrayList = if (list is ArrayList<*>) list else ArrayList(list)
-        @Suppress("UNCHECKED_CAST")
-        bundle.putParcelableArrayList(
-            key,
-            arrayList as ArrayList<Parcelable?>
-        )
-    }
-    return bundle
-}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
index ad973be..988c6f0 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
@@ -31,7 +31,6 @@
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.currentComposer
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.tooling.InspectionTables
 import androidx.compose.ui.R
 import androidx.compose.ui.node.LayoutNode
@@ -166,7 +165,7 @@
 
     private var disposed = false
     private var addedToLifecycle: Lifecycle? = null
-    private var lastContent: @Composable () -> Unit = emptyContent()
+    private var lastContent: @Composable () -> Unit = {}
 
     @OptIn(InternalComposeApi::class)
     override fun setContent(content: @Composable () -> Unit) {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt
index ceb4cf9..6779455 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt
@@ -31,7 +31,6 @@
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -161,7 +160,7 @@
     override val window: Window
 ) : AbstractComposeView(context), DialogWindowProvider {
 
-    private var content: @Composable () -> Unit by mutableStateOf(emptyContent())
+    private var content: @Composable () -> Unit by mutableStateOf({})
 
     protected override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
         private set
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt
index a600e95..050c347 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt
@@ -33,7 +33,6 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -176,7 +175,7 @@
     //  used instead of this custom Layout
     // Get the parent's position, size and layout direction
     Layout(
-        content = emptyContent(),
+        content = {},
         modifier = Modifier.onGloballyPositioned { childCoordinates ->
             val coordinates = childCoordinates.parentLayoutCoordinates!!
             val layoutSize = coordinates.size
@@ -289,7 +288,7 @@
         windowManager.addView(this, params)
     }
 
-    private var content: @Composable () -> Unit by mutableStateOf(emptyContent())
+    private var content: @Composable () -> Unit by mutableStateOf({})
 
     override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
         private set
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
index 73c2797..d2777f9 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
@@ -24,7 +24,6 @@
 import androidx.compose.runtime.RememberObserver
 import androidx.compose.runtime.currentComposer
 import androidx.compose.runtime.ComposeNode
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCompositionReference
 import androidx.compose.ui.Modifier
@@ -148,7 +147,7 @@
         currentIndex++
 
         val nodeState = nodeToNodeState.getOrPut(node) {
-            NodeState(slotId, emptyContent())
+            NodeState(slotId, {})
         }
         val hasPendingChanges = nodeState.composition?.hasInvalidations ?: true
         if (nodeState.content !== content || hasPendingChanges) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionHandles.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionHandles.kt
index 5a98035..67ff734 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionHandles.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionHandles.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.selection
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
 import androidx.compose.runtime.remember
 import androidx.compose.ui.AbsoluteAlignment
 import androidx.compose.ui.Alignment
@@ -187,7 +186,7 @@
     height: Dp,
     onCanvas: DrawScope.() -> Unit
 ) {
-    Layout(emptyContent(), modifier.drawBehind(onCanvas)) { _, _ ->
+    Layout({}, modifier.drawBehind(onCanvas)) { _, _ ->
         // take width and height space of the screen and allow draw modifier to draw inside of it
         layout(width.toIntPx(), height.toIntPx()) {
             // this layout has no children, only draw modifier.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
index a6e4438..9a9fbb5 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
@@ -195,6 +195,11 @@
      * @see SemanticsPropertyReceiver.toggleableState
      */
     val ToggleableState = SemanticsPropertyKey<ToggleableState>("ToggleableState")
+
+    /**
+     * @see SemanticsPropertyReceiver.password
+     */
+    val Password = SemanticsPropertyKey<Unit>("Password")
 }
 
 /**
@@ -361,7 +366,14 @@
     val range: ClosedFloatingPointRange<Float>,
     /*@IntRange(from = 0)*/
     val steps: Int = 0
-)
+) {
+    companion object {
+        /**
+         * Accessibility range information to present indeterminate progress bar
+         */
+        val Indeterminate = ProgressBarRangeInfo(0f, 0f..0f)
+    }
+}
 
 /**
  * The scroll state of one axis if this node is scrollable.
@@ -588,6 +600,13 @@
 by SemanticsProperties.ToggleableState
 
 /**
+ * The node is marked as a password.
+ */
+fun SemanticsPropertyReceiver.password() {
+    this[SemanticsProperties.Password] = Unit
+}
+
+/**
  * Custom actions which are defined by app developers.
  */
 var SemanticsPropertyReceiver.customActions by SemanticsActions.CustomActions
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt
index 210fa44..a7adf1f 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt
@@ -19,7 +19,6 @@
 import androidx.compose.runtime.CompositionReference
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.ambientOf
-import androidx.compose.runtime.emptyContent
 import androidx.compose.ui.platform.Keyboard
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
@@ -69,7 +68,7 @@
     resizable: Boolean = true,
     events: WindowEvents = WindowEvents(),
     onDismissRequest: (() -> Unit)? = null,
-    content: @Composable () -> Unit = emptyContent()
+    content: @Composable () -> Unit = { }
 ) = SwingUtilities.invokeLater {
     AppWindow(
         title = title,
@@ -429,7 +428,7 @@
         }
 
         onCreate(parentComposition) {
-            window.layer.owners?.keyboard = keyboard
+            window.layer.owners.keyboard = keyboard
             content()
         }
 
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt
index ff98afd..f517028 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt
@@ -25,17 +25,16 @@
 import androidx.compose.ui.platform.DesktopComponent
 import androidx.compose.ui.platform.DesktopOwner
 import androidx.compose.ui.platform.DesktopOwners
-import androidx.compose.ui.platform.FrameDispatcher
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.unit.Density
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.swing.Swing
 import org.jetbrains.skija.Canvas
-import org.jetbrains.skija.Picture
-import org.jetbrains.skija.PictureRecorder
-import org.jetbrains.skija.Rect
 import org.jetbrains.skiko.HardwareLayer
 import org.jetbrains.skiko.SkiaLayer
 import org.jetbrains.skiko.SkiaRenderer
-import java.awt.DisplayMode
 import java.awt.Point
 import java.awt.event.FocusEvent
 import java.awt.event.InputMethodEvent
@@ -49,49 +48,51 @@
 import java.awt.im.InputMethodRequests
 
 internal class ComposeLayer {
+    private var isDisposed = false
 
-    private var composition: Composition? = null
+    private val coroutineScope = CoroutineScope(Dispatchers.Swing)
+    // TODO(demin): maybe pass CoroutineScope into AWTDebounceEventQueue and get rid of [cancel]
+    //  method?
     private val events = AWTDebounceEventQueue()
 
-    var owners: DesktopOwners? = null
-        set(value) {
-            field = value
-            renderer = value?.let(::OwnersRenderer)
-        }
-
-    var renderer: Renderer? = null
-
-    private var isDisposed = false
-    private var frameNanoTime = 0L
-    private val frameDispatcher = FrameDispatcher(
-        onFrame = { onFrame(it) },
-        framesPerSecond = ::getFramesPerSecond
+    internal val wrapped = Wrapped()
+    internal val owners: DesktopOwners = DesktopOwners(
+        coroutineScope,
+        wrapped,
+        wrapped::needRedraw
     )
 
-    private val picture = MutableResource<Picture>()
-    private val pictureRecorder = PictureRecorder()
+    private var owner: DesktopOwner? = null
+    private var composition: Composition? = null
 
-    private suspend fun onFrame(nanoTime: Long) {
-        this.frameNanoTime = nanoTime
-        preparePicture(frameNanoTime)
-        wrapped.redrawLayer()
-    }
+    private var content: (@Composable () -> Unit)? = null
+    private var parentComposition: CompositionReference? = null
 
-    var onDensityChanged: ((Density) -> Unit)? = null
-
-    fun onDensityChanged(action: ((Density) -> Unit)?) {
-        onDensityChanged = action
-    }
-
-    private var _density: Density? = null
-    val density
-        get() = _density ?: detectCurrentDensity().also {
-            _density = it
-        }
+    private lateinit var density: Density
 
     inner class Wrapped : SkiaLayer(), DesktopComponent {
         var currentInputMethodRequests: InputMethodRequests? = null
 
+        var isInit = false
+            private set
+
+        override fun init() {
+            super.init()
+            isInit = true
+            resetDensity()
+            initOwner()
+        }
+
+        override fun contentScaleChanged() {
+            super.contentScaleChanged()
+            resetDensity()
+        }
+
+        private fun resetDensity() {
+            this@ComposeLayer.density = detectCurrentDensity()
+            owner?.density = density
+        }
+
         override fun getInputMethodRequests() = currentInputMethodRequests
 
         override fun enableInput(inputMethodRequests: InputMethodRequests) {
@@ -110,33 +111,22 @@
 
         override val density: Density
             get() = this@ComposeLayer.density
-
-        override fun scaleCanvas(dpi: Float) {}
     }
 
-    internal val wrapped = Wrapped()
-
     val component: HardwareLayer
         get() = wrapped
 
     init {
         wrapped.renderer = object : SkiaRenderer {
-            override fun onRender(canvas: Canvas, width: Int, height: Int) {
+            override suspend fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
                 try {
-                    picture.useWithoutClosing {
-                        it?.also(canvas::drawPicture)
-                    }
+                    owners.onFrame(canvas, width, height, nanoTime)
                 } catch (e: Throwable) {
-                    e.printStackTrace(System.err)
                     if (System.getProperty("compose.desktop.render.ignore.errors") == null) {
-                        System.exit(1)
+                        throw e
                     }
                 }
             }
-
-            override fun onDispose() = Unit
-            override fun onInit() = Unit
-            override fun onReshape(width: Int, height: Int) = Unit
         }
         initCanvas()
     }
@@ -145,12 +135,12 @@
         wrapped.addInputMethodListener(object : InputMethodListener {
             override fun caretPositionChanged(event: InputMethodEvent?) {
                 if (event != null) {
-                    owners?.onInputMethodEvent(event)
+                    owners.onInputMethodEvent(event)
                 }
             }
 
             override fun inputMethodTextChanged(event: InputMethodEvent) = events.post {
-                owners?.onInputMethodEvent(event)
+                owners.onInputMethodEvent(event)
             }
         })
 
@@ -158,7 +148,7 @@
             override fun mouseClicked(event: MouseEvent) = Unit
 
             override fun mousePressed(event: MouseEvent) = events.post {
-                owners?.onMousePressed(
+                owners.onMousePressed(
                     (event.x * density.density).toInt(),
                     (event.y * density.density).toInt(),
                     event
@@ -166,7 +156,7 @@
             }
 
             override fun mouseReleased(event: MouseEvent) = events.post {
-                owners?.onMouseReleased(
+                owners.onMouseReleased(
                     (event.x * density.density).toInt(),
                     (event.y * density.density).toInt(),
                     event
@@ -175,7 +165,7 @@
         })
         wrapped.addMouseMotionListener(object : MouseMotionAdapter() {
             override fun mouseDragged(event: MouseEvent) = events.post {
-                owners?.onMouseDragged(
+                owners.onMouseDragged(
                     (event.x * density.density).toInt(),
                     (event.y * density.density).toInt(),
                     event
@@ -183,7 +173,7 @@
             }
 
             override fun mouseMoved(event: MouseEvent) = events.post {
-                owners?.onMouseMoved(
+                owners.onMouseMoved(
                     (event.x * density.density).toInt(),
                     (event.y * density.density).toInt()
                 )
@@ -191,7 +181,7 @@
         })
         wrapped.addMouseWheelListener { event ->
             events.post {
-                owners?.onMouseScroll(
+                owners.onMouseScroll(
                     (event.x * density.density).toInt(),
                     (event.y * density.density).toInt(),
                     event.toComposeEvent()
@@ -200,25 +190,19 @@
         }
         wrapped.addKeyListener(object : KeyAdapter() {
             override fun keyPressed(event: KeyEvent) = events.post {
-                owners?.onKeyPressed(event)
+                owners.onKeyPressed(event)
             }
 
             override fun keyReleased(event: KeyEvent) = events.post {
-                owners?.onKeyReleased(event)
+                owners.onKeyReleased(event)
             }
 
             override fun keyTyped(event: KeyEvent) = events.post {
-                owners?.onKeyTyped(event)
+                owners.onKeyTyped(event)
             }
         })
     }
 
-    private class OwnersRenderer(private val owners: DesktopOwners) : ComposeLayer.Renderer {
-        override suspend fun onFrame(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
-            owners.onFrame(canvas, width, height, nanoTime)
-        }
-    }
-
     private fun MouseWheelEvent.toComposeEvent() = MouseScrollEvent(
         delta = if (scrollType == MouseWheelEvent.WHEEL_BLOCK_SCROLL) {
             MouseScrollUnit.Page((scrollAmount * preciseWheelRotation).toFloat())
@@ -234,90 +218,42 @@
         }
     )
 
-    // We draw into picture, because SkiaLayer.draw can be called from the other thread,
-    // but onRender should be called in AWT thread. Picture doesn't add any visible overhead on
-    // CPU/RAM.
-    private suspend fun preparePicture(frameTimeNanos: Long) {
-        val bounds = Rect.makeWH(wrapped.width * density.density, wrapped.height * density.density)
-        val pictureCanvas = pictureRecorder.beginRecording(bounds)
-        renderer?.onFrame(
-            pictureCanvas,
-            (wrapped.width * density.density).toInt(),
-            (wrapped.height * density.density).toInt(),
-            frameTimeNanos
-        )
-        picture.set(pictureRecorder.finishRecordingAsPicture())
-    }
-
-    fun reinit() {
-        val currentDensity = detectCurrentDensity()
-        if (_density != currentDensity) {
-            _density = currentDensity
-            onDensityChanged?.invoke(density)
-        }
-        check(!isDisposed)
-        wrapped.reinit()
-    }
-
     // TODO(demin): detect OS fontScale
     //  font size can be changed on Windows 10 in Settings - Ease of Access,
     //  on Ubuntu in Settings - Universal Access
     //  on macOS there is no such setting
     private fun detectCurrentDensity(): Density {
-        val density = wrapped.graphicsConfiguration.defaultTransform.scaleX.toFloat()
-        return Density(density, 1f)
-    }
-
-    private fun getFramesPerSecond(): Float {
-        val refreshRate = wrapped.graphicsConfiguration.device.displayMode.refreshRate
-        return if (refreshRate != DisplayMode.REFRESH_RATE_UNKNOWN) refreshRate.toFloat() else 60f
-    }
-
-    fun updateLayer() {
-        check(!isDisposed)
-        wrapped.updateLayer()
+        return Density(wrapped.contentScale, 1f)
     }
 
     fun dispose() {
-        composition?.dispose()
-        events.cancel()
         check(!isDisposed)
-        frameDispatcher.cancel()
-        wrapped.disposeLayer()
-        picture.close()
-        pictureRecorder.close()
+        composition?.dispose()
+        owner?.dispose()
+        events.cancel()
+        coroutineScope.cancel()
+        wrapped.dispose()
         isDisposed = true
     }
 
-    internal fun needRedrawLayer() {
-        check(!isDisposed)
-        frameDispatcher.scheduleFrame()
-    }
-
-    interface Renderer {
-        suspend fun onFrame(canvas: Canvas, width: Int, height: Int, nanoTime: Long)
-    }
-
     internal fun setContent(
-        parent: Any? = null,
-        invalidate: () -> Unit = this::needRedrawLayer,
         parentComposition: CompositionReference? = null,
         content: @Composable () -> Unit
     ) {
-        check(owners == null) {
-            "Cannot setContent twice."
-        }
-        val desktopOwners = DesktopOwners(wrapped, invalidate)
-        val desktopOwner = DesktopOwner(desktopOwners, density)
+        check(!isDisposed)
+        check(this.content == null) { "Cannot set content twice" }
+        this.content = content
+        this.parentComposition = parentComposition
+        // We can't create DesktopOwner now, because we don't know density yet.
+        // We will know density only after SkiaLayer will be visible.
+        initOwner()
+    }
 
-        owners = desktopOwners
-        composition = desktopOwner.setContent(parent = parentComposition, content = content)
-
-        onDensityChanged(desktopOwner::density::set)
-
-        when (parent) {
-            is AppFrame -> parent.onDispose = desktopOwner::dispose
-            is ComposePanel -> parent.onDispose = desktopOwner::dispose
+    private fun initOwner() {
+        check(!isDisposed)
+        if (wrapped.isInit && owner == null && content != null) {
+            owner = DesktopOwner(owners, density)
+            composition = owner!!.setContent(parent = parentComposition, content = content!!)
         }
     }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.kt
index 57d1f10..bf993ee 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.kt
@@ -16,23 +16,23 @@
 package androidx.compose.desktop
 
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.unit.Density
-import java.awt.Graphics
 import java.awt.GridLayout
-import java.awt.event.ComponentAdapter
-import java.awt.event.ComponentEvent
 import javax.swing.JPanel
+import javax.swing.SwingUtilities.isEventDispatchThread
 
 /**
  * ComposePanel is panel for building UI using Compose for Desktop.
  */
-class ComposePanel : JPanel {
-    constructor() : super() {
-        setLayout(GridLayout(1, 1))
+class ComposePanel : JPanel() {
+    init {
+        check(isEventDispatchThread()) {
+            "ComposePanel should be created inside AWT Event Dispatch Thread" +
+                " (use SwingUtilities.invokeLater).\n" +
+                "Creating from another thread isn't supported."
+        }
+        layout = GridLayout(1, 1)
     }
 
-    private var init: Boolean = false
-
     private var layer: ComposeLayer? = null
     private var content: (@Composable () -> Unit)? = null
 
@@ -53,69 +53,34 @@
     private fun initContent() {
         if (layer != null && content != null) {
             layer!!.setContent(
-                parent = this,
-                invalidate = this::needRedrawLayer,
                 content = content!!
             )
         }
     }
 
-    val density: Density
-        get() = if (layer == null) {
-            Density(graphicsConfiguration.defaultTransform.scaleX.toFloat(), 1f)
-        } else {
-            layer!!.density
-        }
-
-    internal var onDispose: (() -> Unit)? = null
-
-    private fun needRedrawLayer() {
-        if (isShowing) {
-            if (!init) {
-                layer!!.updateLayer()
-                init = true
-            }
-            layer!!.needRedrawLayer()
-        }
-    }
-
     override fun addNotify() {
         super.addNotify()
 
         // After [super.addNotify] is called we can safely initialize the layer and composable
         // content.
         layer = ComposeLayer()
-        add(layer!!.wrapped)
-        addComponentListener(object : ComponentAdapter() {
-            override fun componentResized(e: ComponentEvent) {
-                layer?.reinit()
-                needRedrawLayer()
-            }
-        })
+        add(layer!!.component)
 
         initContent()
     }
 
     override fun removeNotify() {
-        super.removeNotify()
-
-        onDispose?.invoke()
         if (layer != null) {
-            remove(layer!!.wrapped)
             layer!!.dispose()
+            remove(layer!!.component)
         }
-        init = false
+
+        super.removeNotify()
     }
 
     override fun requestFocus() {
         if (layer != null) {
-            layer!!.wrapped.requestFocus()
+            layer!!.component.requestFocus()
         }
     }
-
-    override fun paint(g: Graphics?) {
-        super.paint(g)
-        layer?.reinit()
-        needRedrawLayer()
-    }
 }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
index 677caf8..8f21b60 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
@@ -17,26 +17,13 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionReference
-import java.awt.event.ComponentAdapter
-import java.awt.event.ComponentEvent
 import javax.swing.JFrame
 
-class ComposeWindow : JFrame {
-    val parent: AppFrame
+class ComposeWindow(val parent: AppFrame) : JFrame() {
     internal val layer = ComposeLayer()
 
-    val density get() = layer.density
-
-    constructor(parent: AppFrame) : super() {
-        this.parent = parent
-        contentPane.add(layer.wrapped)
-
-        addComponentListener(object : ComponentAdapter() {
-            override fun componentResized(e: ComponentEvent) {
-                layer.reinit()
-                needRedrawLayer()
-            }
-        })
+    init {
+        contentPane.add(layer.component)
     }
 
     /**
@@ -52,27 +39,11 @@
         content: @Composable () -> Unit
     ) {
         layer.setContent(
-            parent = parent,
-            invalidate = this::needRedrawLayer,
             parentComposition = parentComposition,
             content = content
         )
     }
 
-    private fun updateLayer() {
-        if (!isVisible) {
-            return
-        }
-        layer.updateLayer()
-    }
-
-    internal fun needRedrawLayer() {
-        if (!isVisible) {
-            return
-        }
-        layer.needRedrawLayer()
-    }
-
     override fun dispose() {
         layer.dispose()
         super.dispose()
@@ -81,24 +52,7 @@
     override fun setVisible(value: Boolean) {
         if (value != isVisible) {
             super.setVisible(value)
-            layer.wrapped.requestFocus()
-            updateLayer()
-            needRedrawLayer()
+            layer.component.requestFocus()
         }
     }
 }
-
-// Simple FPS tracker for debug purposes
-internal class FPSTracker {
-    private var t0 = 0L
-    private val times = DoubleArray(155)
-    private var timesIdx = 0
-
-    fun track() {
-        val t1 = System.nanoTime()
-        times[timesIdx] = (t1 - t0) / 1000000.0
-        t0 = t1
-        timesIdx = (timesIdx + 1) % times.size
-        println("FPS: ${1000 / times.takeWhile { it > 0 }.average()}")
-    }
-}
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/MutableResource.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/MutableResource.kt
deleted file mode 100644
index 6567d9e..0000000
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/MutableResource.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.desktop
-
-/**
- * Resource that can be used multiple times in parallel threads.
- */
-internal class MutableResource<T : AutoCloseable> : AutoCloseable {
-    @Volatile
-    private var resource: T? = null
-    @Volatile
-    private var resourceToClose: T? = null
-    @Volatile
-    private var usingResource: T? = null
-
-    /**
-     * Close internal resource.
-     *
-     * If we close resource when it is using in [useWithoutClosing], we defer it's closing.
-     *
-     * If resource isn't using we close it immediately.
-     *
-     * After close we can use [set] (for set new internal resource)
-     * and [useWithoutClosing] (it will be called with null resource)
-     */
-    override fun close() = set(null)
-
-    /**
-     * Change internal resource and close previous.
-     *
-     * If we set resource when previous is using in [useWithoutClosing], we defer it's closing.
-     *
-     * If previous isn't using we close it immediately.
-     */
-    fun set(resource: T?): Unit = synchronized(this) {
-        val oldResource = this.resource
-        this.resource = resource
-        if (oldResource === usingResource) {
-            resourceToClose = oldResource
-        } else {
-            oldResource?.close()
-        }
-    }
-
-    /**
-     * Can be called from the other thread.
-     *
-     * If we [set] new resource when we using previous, we close previous after using.
-     */
-    fun useWithoutClosing(use: (T?) -> Unit) {
-        synchronized(this) {
-            usingResource = resource
-        }
-        try {
-            use(usingResource)
-        } finally {
-            synchronized(this) {
-                usingResource = null
-                resourceToClose?.close()
-                resourceToClose = null
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopAnimationClock.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopAnimationClock.kt
deleted file mode 100644
index 6b5b470..0000000
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopAnimationClock.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.platform
-
-import androidx.compose.animation.core.AnimationClockObservable
-import androidx.compose.animation.core.AnimationClockObserver
-import androidx.compose.animation.core.ManualAnimationClock
-
-internal class DesktopAnimationClock(
-    private val invalidate: () -> Unit
-) : AnimationClockObservable {
-    private val manual = ManualAnimationClock(0, dispatchOnSubscribe = false)
-
-    val hasObservers get() = manual.hasObservers
-
-    fun onFrame(nanoTime: Long) {
-        manual.clockTimeMillis = nanoTime / 1_000_000L
-    }
-
-    override fun subscribe(observer: AnimationClockObserver) {
-        manual.subscribe(observer)
-        invalidate()
-    }
-
-    override fun unsubscribe(observer: AnimationClockObserver) {
-        manual.unsubscribe(observer)
-    }
-}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopClipboardManager.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopClipboardManager.kt
index 1a062b6..9e90ea1b 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopClipboardManager.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopClipboardManager.kt
@@ -21,7 +21,7 @@
 import java.awt.datatransfer.DataFlavor
 import java.awt.datatransfer.StringSelection
 
-class DesktopClipboardManager : ClipboardManager {
+internal class DesktopClipboardManager : ClipboardManager {
     internal val systemClipboard = try {
         Toolkit.getDefaultToolkit().getSystemClipboard()
     } catch (e: java.awt.HeadlessException) { null }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopComponent.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopComponent.kt
index 54c59d1..e6ed28f 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopComponent.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopComponent.kt
@@ -20,9 +20,9 @@
 import java.awt.Point
 import java.awt.im.InputMethodRequests
 
-interface DesktopComponent : DesktopInputComponent
+internal interface DesktopComponent : DesktopInputComponent
 
-object DummyDesktopComponent : DesktopComponent {
+internal object DummyDesktopComponent : DesktopComponent {
     override fun enableInput(inputMethodRequests: InputMethodRequests) {}
     override fun disableInput() {}
     override val locationOnScreen = Point(0, 0)
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopHapticFeedback.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopHapticFeedback.kt
index 88b1833..f4d8d4e 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopHapticFeedback.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopHapticFeedback.kt
@@ -20,7 +20,7 @@
 import androidx.compose.ui.hapticfeedback.HapticFeedbackType
 
 // TODO(demin): implement HapticFeedback
-class DesktopHapticFeedback : HapticFeedback {
+internal class DesktopHapticFeedback : HapticFeedback {
     override fun performHapticFeedback(hapticFeedbackType: HapticFeedbackType) {
         println("HapticFeedback.performHapticFeedback not implemented yet")
     }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
index cbeccb4..5427129 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
@@ -19,21 +19,21 @@
 package androidx.compose.ui.platform
 
 import androidx.compose.runtime.ExperimentalComposeApi
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.autofill.Autofill
 import androidx.compose.ui.autofill.AutofillTree
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.focus.FocusDirection
-import androidx.compose.ui.focus.FocusManager
-import androidx.compose.ui.focus.FocusManagerImpl
 import androidx.compose.ui.focus.FocusDirection.Down
 import androidx.compose.ui.focus.FocusDirection.Left
 import androidx.compose.ui.focus.FocusDirection.Next
 import androidx.compose.ui.focus.FocusDirection.Previous
 import androidx.compose.ui.focus.FocusDirection.Right
 import androidx.compose.ui.focus.FocusDirection.Up
+import androidx.compose.ui.focus.FocusManager
+import androidx.compose.ui.focus.FocusManagerImpl
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.DesktopCanvas
@@ -50,11 +50,11 @@
 import androidx.compose.ui.input.key.type
 import androidx.compose.ui.input.mouse.MouseScrollEvent
 import androidx.compose.ui.input.mouse.MouseScrollEventFilter
-import androidx.compose.ui.input.pointer.TestPointerInputEventData
 import androidx.compose.ui.input.pointer.PointerInputEvent
 import androidx.compose.ui.input.pointer.PointerInputEventProcessor
 import androidx.compose.ui.input.pointer.PointerInputFilter
 import androidx.compose.ui.input.pointer.PointerMoveEventFilter
+import androidx.compose.ui.input.pointer.TestPointerInputEventData
 import androidx.compose.ui.layout.RootMeasureBlocks
 import androidx.compose.ui.layout.globalBounds
 import androidx.compose.ui.node.InternalCoreApi
@@ -78,10 +78,10 @@
     ExperimentalComposeApi::class,
     InternalCoreApi::class
 )
-class DesktopOwner(
+internal class DesktopOwner(
     val container: DesktopOwners,
     density: Density = Density(1f, 1f)
-) : Owner, RootForTest {
+) : Owner, RootForTest, DesktopRootForTest {
     internal var size by mutableStateOf(IntSize(0, 0))
 
     override var density by mutableStateOf(density)
@@ -212,7 +212,7 @@
         drawBlock: (Canvas) -> Unit,
         invalidateParentLayer: () -> Unit
     ) = SkijaLayer(
-        this,
+        this::density,
         invalidateParentLayer = {
             invalidateParentLayer()
             container.invalidate()
@@ -252,10 +252,10 @@
         pointerInputEventProcessor.process(event)
     }
 
-    fun processPointerInput(time: Long, pointers: List<TestPointerInputEventData>) {
+    override fun processPointerInput(nanoTime: Long, pointers: List<TestPointerInputEventData>) {
         processPointerInput(
             PointerInputEvent(
-                time,
+                nanoTime,
                 pointers.map { it.toPointerInputEventData() }
             )
         )
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt
index 03c0351..eb9c39a 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwners.kt
@@ -15,34 +15,46 @@
  */
 package androidx.compose.ui.platform
 
+import androidx.compose.animation.core.MonotonicFrameAnimationClock
+import androidx.compose.runtime.BroadcastFrameClock
 import androidx.compose.runtime.Recomposer
+import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.runtime.staticAmbientOf
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.input.key.KeyEvent as ComposeKeyEvent
 import androidx.compose.ui.input.mouse.MouseScrollEvent
 import androidx.compose.ui.input.pointer.PointerId
 import androidx.compose.ui.input.pointer.PointerInputEvent
 import androidx.compose.ui.input.pointer.PointerInputEventData
 import androidx.compose.ui.input.pointer.PointerType
-import androidx.compose.ui.node.InternalCoreApi
-import kotlinx.coroutines.yield
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
 import org.jetbrains.skija.Canvas
 import java.awt.event.InputMethodEvent
 import java.awt.event.KeyEvent
 import java.awt.event.MouseEvent
+import androidx.compose.ui.input.key.KeyEvent as ComposeKeyEvent
 
-val DesktopOwnersAmbient = staticAmbientOf<DesktopOwners>()
+internal val DesktopOwnersAmbient = staticAmbientOf<DesktopOwners>()
 
-@OptIn(InternalCoreApi::class)
-class DesktopOwners(
+@OptIn(ExperimentalCoroutinesApi::class)
+internal class DesktopOwners(
+    coroutineScope: CoroutineScope,
     component: DesktopComponent = DummyDesktopComponent,
-    invalidate: () -> Unit
+    invalidate: () -> Unit = {},
 ) {
     private val _invalidate = invalidate
+    @Volatile
+    private var hasPendingDraws = false
+
+    private var invalidateScheduled = false
     private var willRenderInThisFrame = false
 
     fun invalidate() {
         if (!willRenderInThisFrame) {
+            invalidateScheduled = true
+            hasPendingDraws = true
             _invalidate()
         }
     }
@@ -53,9 +65,32 @@
     private var pointerId = 0L
     private var isMousePressed = false
 
-    internal val animationClock = DesktopAnimationClock(::invalidate)
+    private val dispatcher = FlushCoroutineDispatcher(coroutineScope)
+    private val frameClock = BroadcastFrameClock(::invalidate)
+    private val coroutineContext = dispatcher + frameClock
+
+    internal val animationClock = MonotonicFrameAnimationClock(
+        CoroutineScope(coroutineScope.coroutineContext + coroutineContext)
+    )
+    internal val recomposer = Recomposer(coroutineContext)
     internal val platformInputService: DesktopPlatformInput = DesktopPlatformInput(component)
 
+    init {
+        // TODO(demin): Experimental API (CoroutineStart.UNDISPATCHED).
+        //  Decide what to do before release (copy paste or use different approach).
+        coroutineScope.launch(coroutineContext, start = CoroutineStart.UNDISPATCHED) {
+            recomposer.runRecomposeAndApplyChanges()
+        }
+    }
+
+    /**
+     * Returns true if there are pending recompositions, draws or dispatched tasks.
+     * Can be called from any thread.
+     */
+    fun hasInvalidations() = hasPendingDraws ||
+        recomposer.hasPendingWork ||
+        dispatcher.hasTasks()
+
     fun register(desktopOwner: DesktopOwner) {
         list.add(desktopOwner)
         invalidate()
@@ -66,17 +101,15 @@
         invalidate()
     }
 
-    suspend fun onFrame(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
+    fun onFrame(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
+        invalidateScheduled = false
         willRenderInThisFrame = true
 
         try {
-            animationClock.onFrame(nanoTime)
-
-            // We have to wait recomposition if we want to draw actual animation state
-            // (state can be changed in animationClock.onFrame).
-            // Otherwise there may be a situation when we draw multiple frames with the same
-            // animation state (for example, when FPS always below FPS limit).
-            awaitRecompose()
+            // We must see the actual state before we will render the frame
+            Snapshot.sendApplyNotifications()
+            dispatcher.flush()
+            frameClock.sendFrame(nanoTime)
 
             for (owner in list) {
                 owner.setSize(width, height)
@@ -90,20 +123,12 @@
             owner.draw(canvas)
         }
 
-        if (animationClock.hasObservers) {
+        if (frameClock.hasAwaiters) {
             _invalidate()
         }
-    }
 
-    private suspend fun awaitRecompose() {
-        // We should wait next dispatcher frame because Recomposer doesn't have
-        // pending changes yet, it will only schedule Recomposer.scheduleRecompose in
-        // FrameManager.schedule
-        yield()
-
-        // we can't stuck in infinite loop (because of double dispatching in FrameManager.schedule)
-        while (Recomposer.runningRecomposers.value.any { it.hasPendingWork }) {
-            yield()
+        if (!invalidateScheduled) {
+            hasPendingDraws = false
         }
     }
 
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.kt
index 4ac2ee7..ab23951 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.kt
@@ -42,7 +42,7 @@
 import kotlin.math.max
 import kotlin.math.min
 
-interface DesktopInputComponent {
+internal interface DesktopInputComponent {
     fun enableInput(inputMethodRequests: InputMethodRequests)
     fun disableInput()
     // Input service needs to know this information to implement Input Method support
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopRootForTest.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopRootForTest.kt
new file mode 100644
index 0000000..82a5c1f
--- /dev/null
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopRootForTest.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+import androidx.compose.ui.input.pointer.TestPointerInputEventData
+import androidx.compose.ui.node.RootForTest
+
+/**
+ * The marker interface to be implemented by the desktop root backing the composition.
+ * To be used in tests.
+ */
+interface DesktopRootForTest : RootForTest {
+    /**
+     * Process pointer event
+     *
+     * [nanoTime] time when the pointer event occurred
+     * [pointers] state of all pointers
+     */
+    fun processPointerInput(nanoTime: Long, pointers: List<TestPointerInputEventData>)
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopTextToolbar.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopTextToolbar.kt
index a0b6ee8..a8c5b99 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopTextToolbar.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopTextToolbar.kt
@@ -19,7 +19,7 @@
 import androidx.compose.ui.geometry.Rect
 
 // TODO(demin): implement TextToolbar
-class DesktopTextToolbar : TextToolbar {
+internal class DesktopTextToolbar : TextToolbar {
     override val status: TextToolbarStatus
         get() = TextToolbarStatus.Hidden
 
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopUriHandler.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopUriHandler.kt
index 037b9e52..14430a6 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopUriHandler.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopUriHandler.kt
@@ -17,7 +17,7 @@
 package androidx.compose.ui.platform
 
 // TODO(demin): implement UriHandler
-class DesktopUriHandler : UriHandler {
+internal class DesktopUriHandler : UriHandler {
     override fun openUri(uri: String) {
         println("UriHandler.openUri not implemented yet")
     }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/FlushCoroutineDispatcher.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/FlushCoroutineDispatcher.kt
new file mode 100644
index 0000000..50ab18f65
--- /dev/null
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/FlushCoroutineDispatcher.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Runnable
+import kotlinx.coroutines.launch
+import kotlin.coroutines.CoroutineContext
+
+/**
+ * Dispatcher with the ability to immediately perform (flush) all pending tasks.
+ * Without a flush all tasks are dispatched in the dispatcher provided by [scope]
+ */
+internal class FlushCoroutineDispatcher(
+    private val scope: CoroutineScope
+) : CoroutineDispatcher() {
+    private val tasks = ArrayList<Runnable>()
+    private val tasksCopy = ArrayList<Runnable>()
+
+    override fun dispatch(context: CoroutineContext, block: Runnable) {
+        synchronized(tasks) {
+            val isFlushScheduled = tasks.isNotEmpty()
+            tasks.add(block)
+            if (!isFlushScheduled) {
+                scope.launch { flush() }
+            }
+        }
+    }
+
+    fun hasTasks() = synchronized(tasks) {
+        tasks.isNotEmpty()
+    }
+
+    fun flush() {
+        synchronized(tasks) {
+            tasksCopy.clear()
+            tasksCopy.addAll(tasks)
+            tasks.clear()
+        }
+
+        tasksCopy.forEach(Runnable::run)
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/FrameDispatcher.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/FrameDispatcher.kt
deleted file mode 100644
index e824a52..0000000
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/FrameDispatcher.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.platform
-
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.swing.Swing
-import kotlinx.coroutines.yield
-import kotlin.coroutines.CoroutineContext
-
-/**
- * Dispatch frame after call of [scheduleFrame]
- *
- * Frames executed not more frequent than [framesPerSecond]
- */
-class FrameDispatcher(
-    private val onFrame: suspend (nanoTime: Long) -> Unit,
-    private val framesPerSecond: () -> Float,
-    private val nanoTime: () -> Long = System::nanoTime,
-    context: CoroutineContext = Dispatchers.Swing
-) {
-    private var needFrame = CompletableDeferred<Unit>()
-
-    private val job = GlobalScope.launch(context) {
-        while (true) {
-            needFrame.await()
-            needFrame = CompletableDeferred()
-
-            val frameNanoTime = nanoTime()
-            onFrame(frameNanoTime)
-
-            val elapsed = nanoTime() - frameNanoTime
-            val refreshRate = framesPerSecond()
-            val singleFrameNanos = (1_000_000_000 / refreshRate).toLong()
-            val needToWaitMillis = maxOf(0, singleFrameNanos - elapsed) / 1_000_000
-            delayOrYield(needToWaitMillis)
-        }
-    }
-
-    private suspend fun delayOrYield(millis: Long) {
-        if (millis > 0) {
-            delay(millis)
-        } else {
-            yield()
-        }
-    }
-
-    fun cancel() {
-        job.cancel()
-    }
-
-    fun scheduleFrame() {
-        needFrame.complete(Unit)
-    }
-}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/SkijaLayer.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/SkijaLayer.kt
index d817830..9651619 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/SkijaLayer.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/SkijaLayer.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.ui.platform
 
-import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.DesktopCanvas
@@ -24,17 +24,18 @@
 import androidx.compose.ui.graphics.Outline
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.graphics.asDesktopPath
 import androidx.compose.ui.graphics.nativeCanvas
 import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.graphics.toSkijaRect
-import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.node.OwnedLayer
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.toBounds
 import androidx.compose.ui.unit.toRect
@@ -43,15 +44,15 @@
 import org.jetbrains.skija.Point3
 import org.jetbrains.skija.ShadowUtils
 
-class SkijaLayer(
-    private val owner: DesktopOwner,
+internal class SkijaLayer(
+    private val getDensity: () -> Density,
     private val invalidateParentLayer: () -> Unit,
     private val drawBlock: (Canvas) -> Unit
 ) : OwnedLayer {
     private var size = IntSize.Zero
     private var position = IntOffset.Zero
     private var outlineCache =
-        OutlineCache(owner.density, size, RectangleShape, LayoutDirection.Ltr)
+        OutlineCache(getDensity(), size, RectangleShape, LayoutDirection.Ltr)
     private val matrix = Matrix()
     private val pictureRecorder = PictureRecorder()
     private var picture: Picture? = null
@@ -163,7 +164,7 @@
     }
 
     override fun drawLayer(canvas: Canvas) {
-        outlineCache.density = owner.density
+        outlineCache.density = getDensity()
         if (picture == null) {
             val bounds = size.toBounds().toRect()
             val pictureCanvas = pictureRecorder.beginRecording(bounds.toSkijaRect())
@@ -174,7 +175,7 @@
         canvas.save()
         canvas.concat(matrix)
         canvas.translate(position.x.toFloat(), position.y.toFloat())
-        canvas.nativeCanvas.drawPicture(picture, null, null)
+        canvas.nativeCanvas.drawPicture(picture!!, null, null)
         canvas.restore()
     }
 
@@ -209,7 +210,7 @@
     override fun updateDisplayList() = Unit
 
     @OptIn(ExperimentalUnsignedTypes::class)
-    fun drawShadow(canvas: DesktopCanvas) = with(owner.density) {
+    fun drawShadow(canvas: DesktopCanvas) = with(getDensity()) {
         val path = when (val outline = outlineCache.outline) {
             is Outline.Rectangle -> Path().apply { addRect(outline.rect) }
             is Outline.Rounded -> Path().apply { addRoundRect(outline.roundRect) }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/TestComposeWindow.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/TestComposeWindow.kt
new file mode 100644
index 0000000..48293cd
--- /dev/null
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/TestComposeWindow.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Providers
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.input.mouse.MouseScrollEvent
+import androidx.compose.ui.node.RootForTest
+import androidx.compose.ui.unit.Density
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Runnable
+import kotlinx.coroutines.cancel
+import org.jetbrains.skija.Surface
+import org.jetbrains.skiko.FrameDispatcher
+import kotlin.coroutines.CoroutineContext
+
+private val emptyDispatcher = object : CoroutineDispatcher() {
+    override fun dispatch(context: CoroutineContext, block: Runnable) = Unit
+}
+
+/**
+ * A virtual window for testing purposes.
+ *
+ * After [setContent] it composes content immediately and draws it on a virtual [surface].
+ *
+ * It doesn't dispatch frames by default. If frame dispatching is needed, pass appropriate
+ * dispatcher as coroutineContext (for example, Dispatchers.Swing)
+ */
+class TestComposeWindow(
+    val width: Int,
+    val height: Int,
+    val density: Density = Density(1f, 1f),
+    private val desktopPlatform: DesktopPlatform = DesktopPlatform.Linux,
+    private val nanoTime: () -> Long = System::nanoTime,
+    coroutineContext: CoroutineContext = emptyDispatcher
+) {
+    /**
+     * Virtual surface on which the content will be drawn
+     */
+    val surface = Surface.makeRasterN32Premul(width, height)
+
+    private val canvas = surface.canvas
+    private var owner: DesktopOwner? = null
+
+    private val coroutineScope = CoroutineScope(coroutineContext)
+    private val frameDispatcher: FrameDispatcher = FrameDispatcher(
+        onFrame = { onFrame() },
+        context = coroutineScope.coroutineContext
+    )
+
+    private fun onFrame() {
+        canvas.clear(Color.Transparent.toArgb())
+        owners.onFrame(canvas, width, height, nanoTime())
+    }
+
+    private val owners = DesktopOwners(
+        coroutineScope = coroutineScope,
+        invalidate = frameDispatcher::scheduleFrame
+    )
+
+    /**
+     * All currently registered [RootForTest]s
+     */
+    val roots: Set<DesktopRootForTest> get() = owners.list
+
+    /**
+     * Clear-up all acquired resources and stop all pending work
+     */
+    fun dispose() {
+        owner?.dispose()
+        coroutineScope.cancel()
+    }
+
+    /**
+     * Returns true if there are pending work scheduled by this window
+     */
+    fun hasInvalidations(): Boolean = owners.hasInvalidations()
+
+    /**
+     * Compose [content] immediately and draw it on a [surface]
+     */
+    fun setContent(content: @Composable () -> Unit) {
+        check(owner == null) {
+            "Cannot call setContent twice!"
+        }
+
+        val owner = DesktopOwner(owners, density)
+        owner.setContent {
+            Providers(
+                DesktopPlatformAmbient provides desktopPlatform
+            ) {
+                content()
+            }
+        }
+        owner.setSize(width, height)
+        owner.measureAndLayout()
+        owner.draw(canvas)
+        this.owner = owner
+    }
+
+    /**
+     * Process mouse scroll event
+     */
+    fun onMouseScroll(x: Int, y: Int, event: MouseScrollEvent) {
+        owners.onMouseScroll(x, y, event)
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
index 772faee..aa4e972 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
@@ -18,51 +18,10 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composition
 import androidx.compose.runtime.CompositionReference
-import androidx.compose.runtime.DefaultMonotonicFrameClock
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.Providers
-import androidx.compose.runtime.Recomposer
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.node.LayoutNode
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.NonCancellable
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.swing.Swing
-import javax.swing.SwingUtilities
-
-object SwingEmbeddingContext {
-    fun isMainThread(): Boolean {
-        return SwingUtilities.isEventDispatchThread()
-    }
-
-    fun mainThreadCompositionContext(): CoroutineContext {
-        return Dispatchers.Swing + DefaultMonotonicFrameClock
-    }
-}
-
-// TODO: Replace usages with an appropriately scoped implementation
-// Below is a local copy of the old Recomposer.current() implementation.
-@OptIn(ExperimentalCoroutinesApi::class)
-private val GlobalDefaultRecomposer = run {
-    val embeddingContext = SwingEmbeddingContext
-    val mainScope = CoroutineScope(
-        NonCancellable + embeddingContext.mainThreadCompositionContext()
-    )
-
-    Recomposer(mainScope.coroutineContext).also {
-        // NOTE: Launching undispatched so that compositions created with the
-        // singleton instance can assume the recomposer is running
-        // when they perform initial composition. The relevant Recomposer code is
-        // appropriately thread-safe for this.
-        mainScope.launch(start = CoroutineStart.UNDISPATCHED) {
-            it.runRecomposeAndApplyChanges()
-        }
-    }
-}
 
 /**
  * Composes the given composable into [DesktopOwner]
@@ -72,17 +31,13 @@
  * @param content A `@Composable` function declaring the UI contents
  */
 @OptIn(ExperimentalComposeApi::class)
-fun DesktopOwner.setContent(
+internal fun DesktopOwner.setContent(
     parent: CompositionReference? = null,
     content: @Composable () -> Unit
 ): Composition {
     GlobalSnapshotManager.ensureStarted()
 
-    val composition = Composition(
-        root,
-        DesktopUiApplier(root),
-        parent ?: GlobalDefaultRecomposer
-    )
+    val composition = Composition(root, DesktopUiApplier(root), parent ?: container.recomposer)
     composition.setContent {
         ProvideDesktopAmbients(this) {
             DesktopSelectionContainer(content)
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/FrameDispatcherTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/FrameDispatcherTest.kt
deleted file mode 100644
index 25dddc3..0000000
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/FrameDispatcherTest.kt
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.desktop
-
-import androidx.compose.ui.platform.FrameDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.test.TestCoroutineScope
-import kotlinx.coroutines.test.runBlockingTest
-import org.junit.Assert.assertEquals
-import org.junit.Test
-
-@OptIn(ExperimentalCoroutinesApi::class)
-internal class FrameDispatcherTest {
-    var frameIndex = 0
-    var frameTime = 0L
-
-    var frameDuration = 0L
-
-    suspend fun onFrame(nanoTime: Long) {
-        frameIndex++
-        frameTime = nanoTime / 1_000_000
-        delay(frameDuration)
-    }
-
-    fun TestCoroutineScope.testFrameDispatcher() = FrameDispatcher(
-        ::onFrame,
-        framesPerSecond = { 100f }, // one frame is 10 milliseconds
-        nanoTime = { currentTime * 1_000_000 },
-        coroutineContext
-    )
-
-    @Test
-    fun `don't schedule`() = runBlockingTest {
-        val timer = testFrameDispatcher()
-
-        runCurrent()
-        assertEquals(0, currentTime)
-        assertEquals(0, frameIndex)
-        assertEquals(0, frameTime)
-
-        advanceTimeBy(10_000)
-        assertEquals(0, frameIndex)
-        assertEquals(0, frameTime)
-
-        timer.cancel()
-    }
-
-    @Test
-    fun `schedule one time`() = runBlockingTest {
-        val timer = testFrameDispatcher()
-
-        advanceTimeBy(1234)
-        timer.scheduleFrame()
-        assertEquals(1, frameIndex)
-        assertEquals(1234, frameTime)
-
-        advanceTimeBy(10_000)
-        assertEquals(1, frameIndex)
-        assertEquals(1234, frameTime)
-
-        timer.cancel()
-    }
-
-    @Test
-    fun `schedule multiple times`() = runBlockingTest {
-        val timer = testFrameDispatcher()
-
-        advanceTimeBy(10_000)
-        timer.scheduleFrame()
-
-        timer.scheduleFrame()
-        timer.scheduleFrame()
-        assertEquals(1, frameIndex)
-        assertEquals(10_000, frameTime)
-
-        advanceTimeBy(9)
-        assertEquals(1, frameIndex)
-        assertEquals(10_000, frameTime)
-
-        advanceTimeBy(1)
-        assertEquals(2, frameIndex)
-        assertEquals(10_010, frameTime)
-
-        advanceTimeBy(10_000)
-        assertEquals(2, frameIndex)
-        assertEquals(10_010, frameTime)
-
-        timer.cancel()
-    }
-
-    @Test
-    fun `schedule after short delay`() = runBlockingTest {
-        val timer = testFrameDispatcher()
-
-        advanceTimeBy(10_000)
-        timer.scheduleFrame()
-
-        advanceTimeBy(5)
-        timer.scheduleFrame()
-        assertEquals(1, frameIndex)
-        assertEquals(10_000, frameTime)
-
-        advanceTimeBy(4)
-        timer.scheduleFrame()
-        assertEquals(1, frameIndex)
-        assertEquals(10_000, frameTime)
-
-        advanceTimeBy(1)
-        timer.scheduleFrame()
-        assertEquals(2, frameIndex)
-        assertEquals(10_010, frameTime)
-
-        timer.cancel()
-    }
-
-    @Test
-    fun `schedule after long delay`() = runBlockingTest {
-        val timer = testFrameDispatcher()
-
-        advanceTimeBy(10_000)
-        timer.scheduleFrame()
-
-        advanceTimeBy(10_000)
-        timer.scheduleFrame()
-        assertEquals(2, frameIndex)
-        assertEquals(20_000, frameTime)
-
-        timer.cancel()
-    }
-
-    @Test
-    fun `schedule after short frame`() = runBlockingTest {
-        val timer = testFrameDispatcher()
-        frameDuration = 7
-
-        advanceTimeBy(10_000)
-        timer.scheduleFrame()
-        assertEquals(1, frameIndex)
-        assertEquals(10_000, frameTime)
-
-        timer.scheduleFrame()
-        assertEquals(1, frameIndex)
-        assertEquals(10_000, frameTime)
-
-        advanceTimeBy(9)
-        timer.scheduleFrame()
-        assertEquals(1, frameIndex)
-        assertEquals(10_000, frameTime)
-
-        advanceTimeBy(1)
-        assertEquals(2, frameIndex)
-        assertEquals(10_010, frameTime)
-
-        advanceTimeBy(10_000)
-        assertEquals(2, frameIndex)
-        assertEquals(10_010, frameTime)
-
-        timer.cancel()
-    }
-
-    @Test
-    fun `schedule after long frame`() = runBlockingTest {
-        val timer = testFrameDispatcher()
-        frameDuration = 13
-
-        advanceTimeBy(10_000)
-        timer.scheduleFrame()
-        assertEquals(1, frameIndex)
-        assertEquals(10_000, frameTime)
-
-        timer.scheduleFrame()
-        assertEquals(1, frameIndex)
-        assertEquals(10_000, frameTime)
-
-        advanceTimeBy(12)
-        timer.scheduleFrame()
-        assertEquals(1, frameIndex)
-        assertEquals(10_000, frameTime)
-
-        advanceTimeBy(1)
-        assertEquals(2, frameIndex)
-        assertEquals(10_013, frameTime)
-
-        advanceTimeBy(10_000)
-        assertEquals(2, frameIndex)
-        assertEquals(10_013, frameTime)
-
-        timer.cancel()
-    }
-
-    @Test
-    fun cancel() = runBlockingTest {
-        val timer = testFrameDispatcher()
-
-        advanceTimeBy(10_000)
-        timer.scheduleFrame()
-
-        timer.scheduleFrame()
-        timer.scheduleFrame()
-        timer.cancel()
-        advanceTimeBy(10_000)
-        assertEquals(1, frameIndex)
-        assertEquals(10_000, frameTime)
-    }
-}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/MutableResourceTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/MutableResourceTest.kt
deleted file mode 100644
index 078976f..0000000
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/MutableResourceTest.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.desktop
-
-import org.junit.Assert.assertTrue
-import org.junit.Test
-
-internal class MutableResourceTest {
-    private val allResources = ArrayList<TestResource>()
-
-    private inner class TestResource : AutoCloseable {
-        var isClosed = false
-            private set
-
-        init {
-            allResources.add(this)
-        }
-
-        fun checkIsAvailable() {
-            for (i in 0 until 10) {
-                check(!isClosed)
-                Thread.sleep(0)
-            }
-        }
-
-        override fun close() {
-            isClosed = true
-        }
-    }
-
-    @Test(timeout = 5000)
-    fun `set and use in parallel threads`() {
-        val resource = MutableResource<TestResource>()
-
-        val usingThread = TestThread {
-            for (i in 0 until 10000) {
-                resource.useWithoutClosing {
-                    it?.checkIsAvailable()
-                }
-            }
-        }
-
-        val swappingTread = TestThread {
-            while (true) {
-                resource.set(TestResource())
-                Thread.sleep(0)
-                resource.set(TestResource())
-                Thread.sleep(0)
-                resource.close()
-                Thread.sleep(0)
-            }
-        }
-
-        usingThread.start()
-        swappingTread.start()
-
-        usingThread.joinAndThrow()
-        swappingTread.interrupt()
-        swappingTread.joinAndThrow()
-
-        resource.close()
-        assertTrue(allResources.all(TestResource::isClosed))
-    }
-}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilterTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilterTest.kt
index d743d95..8f8e1a4 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilterTest.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilterTest.kt
@@ -20,10 +20,10 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
+import androidx.compose.ui.platform.TestComposeWindow
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.test.TestComposeWindow
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -50,7 +50,7 @@
             )
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(3f), Orientation.Vertical)
@@ -78,7 +78,7 @@
             )
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 20,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(3f), Orientation.Vertical)
@@ -116,7 +116,7 @@
             )
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(3f), Orientation.Horizontal)
@@ -153,7 +153,7 @@
             )
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(3f), Orientation.Horizontal)
@@ -184,7 +184,7 @@
             )
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(3f), Orientation.Horizontal)
@@ -224,7 +224,7 @@
             }
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Line(-1f), Orientation.Horizontal)
@@ -262,7 +262,7 @@
             }
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Page(1f), Orientation.Horizontal)
@@ -295,7 +295,7 @@
             }
         }
 
-        window.owners.onMouseScroll(
+        window.onMouseScroll(
             x = 0,
             y = 0,
             event = MouseScrollEvent(MouseScrollUnit.Page(1f), Orientation.Horizontal)
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt
index 950d856..cb7efda 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt
@@ -32,6 +32,7 @@
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
 import androidx.compose.runtime.ExperimentalComposeApi
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -47,9 +48,10 @@
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.test.junit4.DesktopScreenshotTestRule
 import androidx.compose.ui.unit.dp
+import com.google.common.truth.Truth
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.yield
 import org.junit.Assert.assertFalse
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 
@@ -209,7 +211,6 @@
     }
 
     @Test(timeout = 5000)
-    @Ignore("enable after we fix https://github.com/JetBrains/compose-jb/issues/137")
     fun `rendering of transition`() = renderingTest(width = 40, height = 40) {
         var targetValue by mutableStateOf(10f)
 
@@ -221,34 +222,34 @@
             Box(Modifier.size(value.dp).background(Color.Blue))
         }
 
+        // TODO(demin) can we get rid of 'yield' here and write something more meaningful?
+        // animateFloatAsState will remember the initial animation value
+        // only after two asynchronous points:
+        //
+        // 1. LaunchedEffect in animateFloatAsState (wait with 'yield')
+        // 2. startTimeSpecified/withFrameNanos in SuspendAnimation.kt (wait with 'awaitNextRender')
+        //
+        // We need to wait for this moment because later we will change the target value
+        // to animate to it from the initial remembered value.
+        yield()
         awaitNextRender()
         screenshotRule.snap(surface, "frame1_initial")
 
+        targetValue = 40f
+        awaitNextRender()
+        screenshotRule.snap(surface, "frame2_target40_0ms")
+
+        currentTimeMillis = 10
+        awaitNextRender()
+        screenshotRule.snap(surface, "frame3_target40_10ms")
+
         currentTimeMillis = 20
         awaitNextRender()
-        screenshotRule.snap(surface, "frame2_20ms")
+        screenshotRule.snap(surface, "frame4_target40_20ms")
 
         currentTimeMillis = 30
         awaitNextRender()
-        screenshotRule.snap(surface, "frame3_30ms")
-        assertFalse(hasRenders())
-
-        targetValue = 40f
-        currentTimeMillis = 30
-        awaitNextRender()
-        screenshotRule.snap(surface, "frame4_30ms_target40")
-
-        currentTimeMillis = 40
-        awaitNextRender()
-        screenshotRule.snap(surface, "frame5_40ms_target40")
-
-        currentTimeMillis = 50
-        awaitNextRender()
-        screenshotRule.snap(surface, "frame6_50ms_target40")
-
-        currentTimeMillis = 60
-        awaitNextRender()
-        screenshotRule.snap(surface, "frame7_60ms_target40")
+        screenshotRule.snap(surface, "frame5_target40_30ms")
         assertFalse(hasRenders())
     }
 
@@ -328,7 +329,6 @@
     }
 
     @Test(timeout = 5000)
-    @Ignore("enable after we fix https://github.com/JetBrains/compose-jb/issues/137")
     fun `rendering, change state before first onRender`() = renderingTest(
         width = 40,
         height = 40
@@ -343,4 +343,18 @@
         screenshotRule.snap(surface, "frame1_initial")
         assertFalse(hasRenders())
     }
+
+    @Test(timeout = 5000)
+    fun `launch effect`() = renderingTest(width = 40, height = 40) {
+        var effectIsLaunched = false
+
+        setContent {
+            LaunchedEffect(Unit) {
+                effectIsLaunched = true
+            }
+        }
+
+        awaitNextRender()
+        Truth.assertThat(effectIsLaunched).isTrue()
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/GraphicsLayerTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/GraphicsLayerTest.kt
index b637b80..79dd096 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/GraphicsLayerTest.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/GraphicsLayerTest.kt
@@ -25,7 +25,6 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.test.TestComposeWindow
 import androidx.compose.ui.test.junit4.DesktopScreenshotTestRule
 import androidx.compose.ui.unit.dp
 import org.junit.Rule
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt
index 9f966fa..f374ad0 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt
@@ -21,46 +21,60 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.toArgb
 import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancel
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.swing.Swing
 import kotlinx.coroutines.yield
 import org.jetbrains.skija.Canvas
 import org.jetbrains.skija.Surface
+import org.jetbrains.skiko.FrameDispatcher
+import kotlin.coroutines.CoroutineContext
 
+@OptIn(ExperimentalCoroutinesApi::class)
 internal fun renderingTest(
     width: Int,
     height: Int,
     platform: DesktopPlatform = DesktopPlatform.Linux,
+    context: CoroutineContext = Dispatchers.Swing,
     block: suspend RenderingTestScope.() -> Unit
-) = runBlocking(Dispatchers.Main) {
-    val scope = RenderingTestScope(width, height, platform)
+) = runBlocking(context) {
+    val scope = RenderingTestScope(width, height, platform, context)
     try {
         scope.block()
     } finally {
-        scope.owner?.dispose()
-        scope.frameDispatcher.cancel()
+        scope.dispose()
     }
 }
 
 internal class RenderingTestScope(
     private val width: Int,
     private val height: Int,
-    private val platform: DesktopPlatform
+    private val platform: DesktopPlatform,
+    coroutineContext: CoroutineContext
 ) {
     var currentTimeMillis = 0L
 
-    val frameDispatcher = FrameDispatcher(
-        onFrame = { onRender(it) },
-        framesPerSecond = { Float.POSITIVE_INFINITY },
-        nanoTime = { currentTimeMillis * 1_000_000 }
-    )
+    private val coroutineScope = CoroutineScope(coroutineContext)
+    private val frameDispatcher = FrameDispatcher(coroutineContext) {
+        onRender(currentTimeMillis * 1_000_000)
+    }
 
-    val surface: Surface = Surface.makeRasterN32Premul(width, height)!!
+    val surface: Surface = Surface.makeRasterN32Premul(width, height)
     val canvas: Canvas = surface.canvas
     val owners = DesktopOwners(
+        coroutineScope = coroutineScope,
         invalidate = frameDispatcher::scheduleFrame
     )
-    var owner: DesktopOwner? = null
+    private var owner: DesktopOwner? = null
+
+    fun dispose() {
+        owner?.dispose()
+        frameDispatcher.cancel()
+        coroutineScope.cancel()
+    }
 
     private var onRender = CompletableDeferred<Unit>()
 
@@ -75,7 +89,7 @@
         this.owner = owner
     }
 
-    private suspend fun onRender(timeNanos: Long) {
+    private fun onRender(timeNanos: Long) {
         canvas.clear(Color.Transparent.toArgb())
         owners.onFrame(canvas, width, height, timeNanos)
         onRender.complete(Unit)
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/SkijaLayerTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/SkijaLayerTest.kt
index 637863c..ced4b1a 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/SkijaLayerTest.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/SkijaLayerTest.kt
@@ -22,6 +22,7 @@
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
@@ -315,12 +316,12 @@
     }
 
     private fun TestSkijaLayer() = SkijaLayer(
-        owner = DesktopOwner(DesktopOwners(invalidate = {})),
+        { Density(1f, 1f) },
         invalidateParentLayer = {},
         drawBlock = {}
     )
 
-    fun SkijaLayer.updateProperties(
+    private fun SkijaLayer.updateProperties(
         scaleX: Float = 1f,
         scaleY: Float = 1f,
         alpha: Float = 1f,
diff --git a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTouchEventTest.java b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTouchEventTest.java
index a98ea78..ff7307e 100644
--- a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTouchEventTest.java
+++ b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorLayoutTouchEventTest.java
@@ -40,6 +40,7 @@
 import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -115,6 +116,7 @@
     }
 
     @Test
+    @Ignore // This test is broken on all SDK levels.
     public void onInterceptTouchEvent_interceptOnDown() throws Throwable {
         mView1.setOnTouchListener(new ReturnTrueOnTouchListener());
         final MotionEvent downEvent = obtainEvent(MotionEvent.ACTION_DOWN);
@@ -209,6 +211,7 @@
     }
 
     @Test
+    @Ignore // This test is broken on all SDK levels.
     public void onInterceptTouchEvent_multipleDowns_intercepted() throws Throwable {
         mView1.setOnTouchListener(new ReturnTrueOnTouchListener());
         final MotionEvent downEvent = obtainEvent(MotionEvent.ACTION_DOWN);
@@ -350,6 +353,7 @@
     }
 
     @Test
+    @Ignore // This test is broken on all SDK levels.
     public void blocksInteractionBelow_move() throws Throwable {
         mView1.setOnTouchListener(new ReturnTrueOnTouchListener());
         final MotionEvent downEvent = obtainEvent(MotionEvent.ACTION_DOWN);
@@ -414,6 +418,7 @@
     }
 
     @Test
+    @Ignore // This test is broken on all SDK levels.
     public void onTouchEvent_intercept() throws Throwable {
         TouchDelegate touchDelegate = mock(TouchDelegate.class);
         mCoordinatorLayout.setTouchDelegate(touchDelegate);
diff --git a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
index c246488..01d5bd8 100644
--- a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
+++ b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
@@ -50,8 +50,10 @@
 
     @After
     @UiThreadTest
-    public void teardown() throws Throwable {
-        mCoordinatorLayout.removeView(mBar);
+    public void teardown() {
+        if (mCoordinatorLayout != null) {
+            mCoordinatorLayout.removeView(mBar);
+        }
     }
 
     /**
diff --git a/core/core/api/res-1.5.0-beta01.txt b/core/core/api/res-1.5.0-beta01.txt
index a609e0a..43cbfa4 100644
--- a/core/core/api/res-1.5.0-beta01.txt
+++ b/core/core/api/res-1.5.0-beta01.txt
@@ -6,6 +6,7 @@
 attr fontProviderFetchTimeout
 attr fontProviderPackage
 attr fontProviderQuery
+attr fontProviderSystemFontFamily
 attr fontStyle
 attr fontVariationSettings
 attr fontWeight
diff --git a/core/core/api/res-current.txt b/core/core/api/res-current.txt
index a609e0a..43cbfa4 100644
--- a/core/core/api/res-current.txt
+++ b/core/core/api/res-current.txt
@@ -6,6 +6,7 @@
 attr fontProviderFetchTimeout
 attr fontProviderPackage
 attr fontProviderQuery
+attr fontProviderSystemFontFamily
 attr fontStyle
 attr fontVariationSettings
 attr fontWeight
diff --git a/core/core/src/androidTest/java/androidx/core/content/res/FontResourcesParserCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/res/FontResourcesParserCompatTest.java
index cc09f91..b98fa33 100644
--- a/core/core/src/androidTest/java/androidx/core/content/res/FontResourcesParserCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/res/FontResourcesParserCompatTest.java
@@ -155,6 +155,7 @@
                 request.getProviderAuthority());
         assertEquals("androidx.core.test", request.getProviderPackage());
         assertEquals("singleFontFamily", request.getQuery());
+        assertEquals("serif", providerEntry.getSystemFontFamilyName());
     }
 
     @Test
diff --git a/core/core/src/androidTest/java/androidx/core/content/res/ResourcesCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/res/ResourcesCompatTest.java
index 5fdc9ff..f218c86 100644
--- a/core/core/src/androidTest/java/androidx/core/content/res/ResourcesCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/res/ResourcesCompatTest.java
@@ -455,6 +455,13 @@
     }
 
     @Test
+    public void testSystemFontFamilyReturnsSystemFont() {
+        Typeface typeface = ResourcesCompat.getFont(mContext, R.font.samplexmldownloadedfont);
+        assertNotNull(typeface);
+        assertEquals(typeface,  Typeface.create("serif", Typeface.NORMAL));
+    }
+
+    @Test
     public void getFloatForFloat() {
         float value = ResourcesCompat.getFloat(mResources, R.dimen.twelve_point_five);
         assertEquals(12.5f, value, 0.01f);
diff --git a/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java b/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java
index 684dd8b..9c00ab6 100644
--- a/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java
@@ -141,7 +141,8 @@
         final FontRequest parsedRequest = entry.getRequest();
         final FontRequest request = new FontRequest(parsedRequest.getProviderAuthority(),
                 parsedRequest.getProviderPackage(), parsedRequest.getQuery(), SIGNATURE);
-        return new ProviderResourceEntry(request, entry.getFetchStrategy(), entry.getTimeout());
+        return new ProviderResourceEntry(request, entry.getFetchStrategy(), entry.getTimeout(),
+                entry.getSystemFontFamilyName());
     }
 
     public static class FontCallback extends ResourcesCompat.FontCallback {
diff --git a/core/core/src/androidTest/res/font/samplexmldownloadedfont.xml b/core/core/src/androidTest/res/font/samplexmldownloadedfont.xml
index cba386f..4e397aa 100644
--- a/core/core/src/androidTest/res/font/samplexmldownloadedfont.xml
+++ b/core/core/src/androidTest/res/font/samplexmldownloadedfont.xml
@@ -3,5 +3,6 @@
     app:fontProviderAuthority="androidx.core.provider.fonts.font"
     app:fontProviderPackage="androidx.core.test"
     app:fontProviderQuery="singleFontFamily"
-    app:fontProviderCerts="@array/mock_provider_certs">
+    app:fontProviderCerts="@array/mock_provider_certs"
+    app:fontProviderSystemFontFamily="serif">
 </font-family>
diff --git a/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java b/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
index 79c1625..04ec6b1 100644
--- a/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
+++ b/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
@@ -16,6 +16,7 @@
 
 package androidx.core.content.res;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 
 import android.content.res.Resources;
@@ -77,12 +78,21 @@
         private final @NonNull FontRequest mRequest;
         private final int mTimeoutMs;
         private final @FetchStrategy int mStrategy;
+        private final @Nullable String mSystemFontFamilyName;
 
+        /** @hide */
+        @RestrictTo(LIBRARY)
         public ProviderResourceEntry(@NonNull FontRequest request, @FetchStrategy int strategy,
-                int timeoutMs) {
+                int timeoutMs, @Nullable String systemFontFamilyName) {
             mRequest = request;
             mStrategy = strategy;
             mTimeoutMs = timeoutMs;
+            mSystemFontFamilyName = systemFontFamilyName;
+        }
+
+        public ProviderResourceEntry(@NonNull FontRequest request, @FetchStrategy int strategy,
+                int timeoutMs) {
+            this(request, strategy, timeoutMs, null /*systemFontFamilyName*/);
         }
 
         public @NonNull FontRequest getRequest() {
@@ -96,6 +106,12 @@
         public int getTimeout() {
             return mTimeoutMs;
         }
+
+        /** @hide */
+        @RestrictTo(LIBRARY)
+        public @Nullable String getSystemFontFamilyName() {
+            return mSystemFontFamilyName;
+        }
     }
 
     /**
@@ -200,6 +216,9 @@
                 FETCH_STRATEGY_ASYNC);
         int timeoutMs = array.getInteger(R.styleable.FontFamily_fontProviderFetchTimeout,
                 DEFAULT_TIMEOUT_MILLIS);
+        String systemFontFamilyName = array
+                .getString(R.styleable.FontFamily_fontProviderSystemFontFamily);
+
         array.recycle();
         if (authority != null && providerPackage != null && query != null) {
             while (parser.next() != XmlPullParser.END_TAG) {
@@ -207,7 +226,11 @@
             }
             List<List<byte[]>> certs = readCerts(resources, certsId);
             return new ProviderResourceEntry(
-                    new FontRequest(authority, providerPackage, query, certs), strategy, timeoutMs);
+                    new FontRequest(authority, providerPackage, query, certs),
+                    strategy,
+                    timeoutMs,
+                    systemFontFamilyName
+            );
         }
         List<FontFileResourceEntry> fonts = new ArrayList<>();
         while (parser.next() != XmlPullParser.END_TAG) {
diff --git a/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java b/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
index c29f838..f91741c7 100644
--- a/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
+++ b/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
@@ -94,6 +94,20 @@
     }
 
     /**
+     * Returns Typeface if the system has the font family with the name [familyName]. For example
+     * querying with "sans-serif" would check if the "sans-serif" family is defined in the system
+     * and return the Typeface if so.
+     *
+     * @param familyName The name of the font family.
+     */
+    private static Typeface getSystemFontFamily(@Nullable String familyName) {
+        if (familyName == null || familyName.isEmpty()) return null;
+        Typeface typeface = Typeface.create(familyName, Typeface.NORMAL);
+        Typeface defaultTypeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
+        return typeface != null && !typeface.equals(defaultTypeface) ? typeface : null;
+    }
+
+    /**
      * Create Typeface from XML resource which root node is font-family.
      *
      * @return null if failed to create.
@@ -109,6 +123,16 @@
         Typeface typeface;
         if (entry instanceof ProviderResourceEntry) {
             ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
+
+            Typeface fontFamilyTypeface = getSystemFontFamily(
+                    providerEntry.getSystemFontFamilyName());
+            if (fontFamilyTypeface != null) {
+                if (fontCallback != null) {
+                    fontCallback.callbackSuccessAsync(fontFamilyTypeface, handler);
+                }
+                return fontFamilyTypeface;
+            }
+
             final boolean isBlocking = isRequestFromLayoutInflator
                     ? providerEntry.getFetchStrategy()
                     == FontResourcesParserCompat.FETCH_STRATEGY_BLOCKING
diff --git a/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java b/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
index f0ab4ec..e45b275 100644
--- a/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
+++ b/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
@@ -173,7 +173,7 @@
 
     private static final int BACKGROUND_THREAD_KEEP_ALIVE_DURATION_MS = 10000;
     private static final SelfDestructiveThread sBackgroundThread =
-            new SelfDestructiveThread("fonts", Process.THREAD_PRIORITY_BACKGROUND,
+            new SelfDestructiveThread("fonts-androidx", Process.THREAD_PRIORITY_BACKGROUND,
                     BACKGROUND_THREAD_KEEP_ALIVE_DURATION_MS);
 
     @NonNull
diff --git a/core/core/src/main/res/values/attrs.xml b/core/core/src/main/res/values/attrs.xml
index d1ff95c..ce64ab9 100644
--- a/core/core/src/main/res/values/attrs.xml
+++ b/core/core/src/main/res/values/attrs.xml
@@ -58,6 +58,11 @@
               timeout and wait until a reply is received from the font provider. -->
             <enum name="forever" value="-1" />
         </attr>
+        <!-- Provides the system font family name to check before downloading the font. For
+        example if the fontProviderQuery asked for "Sans Serif", it is possible to define
+        fontProviderSystemFontFamily as "sans-serif" to tell the system to use "sans-serif" font
+        family if it exists on the system. -->
+        <attr name="fontProviderSystemFontFamily" format="string" />
     </declare-styleable>
 
     <!-- Attributes that are read when parsing a <font> tag, which is a child of
diff --git a/core/core/src/main/res/values/public.xml b/core/core/src/main/res/values/public.xml
index 64049c8..b27ee48 100644
--- a/core/core/src/main/res/values/public.xml
+++ b/core/core/src/main/res/values/public.xml
@@ -24,6 +24,7 @@
     <public type="attr" name="fontProviderCerts"/>
     <public type="attr" name="fontProviderFetchStrategy"/>
     <public type="attr" name="fontProviderFetchTimeout"/>
+    <public type="attr" name="fontProviderSystemFontFamily"/>
     <public type="attr" name="fontStyle"/>
     <public type="attr" name="font"/>
     <public type="attr" name="fontWeight"/>
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 33e3fd2..907933a 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -129,6 +129,7 @@
     docs("androidx.lifecycle:lifecycle-reactivestreams-ktx:2.3.0")
     docs("androidx.lifecycle:lifecycle-runtime:2.3.0")
     docs("androidx.lifecycle:lifecycle-runtime-ktx:2.3.0")
+    docs("androidx.lifecycle:lifecycle-runtime-testing:2.3.0")
     docs("androidx.lifecycle:lifecycle-service:2.3.0")
     docs("androidx.lifecycle:lifecycle-viewmodel:2.3.0")
     docs("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0")
diff --git a/fragment/fragment-ktx/build.gradle b/fragment/fragment-ktx/build.gradle
index 88435eb..a5b8a4a 100644
--- a/fragment/fragment-ktx/build.gradle
+++ b/fragment/fragment-ktx/build.gradle
@@ -36,11 +36,11 @@
     api("androidx.collection:collection-ktx:1.1.0") {
         because 'Mirror fragment dependency graph for -ktx artifacts'
     }
-    api(projectOrArtifact(":lifecycle:lifecycle-livedata-core-ktx")) {
+    api("androidx.lifecycle:lifecycle-livedata-core-ktx:2.3.0") {
         because 'Mirror fragment dependency graph for -ktx artifacts'
     }
-    api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-ktx"))
-    api(projectOrArtifact(":savedstate:savedstate-ktx")) {
+    api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0")
+    api("androidx.savedstate:savedstate-ktx:1.1.0") {
         because 'Mirror fragment dependency graph for -ktx artifacts'
     }
     api(KOTLIN_STDLIB)
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index a6c2996..a31aa62 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -38,10 +38,10 @@
     api("androidx.viewpager:viewpager:1.0.0")
     api("androidx.loader:loader:1.0.0")
     api(projectOrArtifact(":activity:activity"))
-    api(projectOrArtifact(":lifecycle:lifecycle-livedata-core"))
-    api(projectOrArtifact(":lifecycle:lifecycle-viewmodel"))
-    api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-savedstate"))
-    api(projectOrArtifact(":savedstate:savedstate"))
+    api("androidx.lifecycle:lifecycle-livedata-core:2.3.0")
+    api("androidx.lifecycle:lifecycle-viewmodel:2.3.0")
+    api("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0")
+    api("androidx.savedstate:savedstate:1.1.0")
     api("androidx.annotation:annotation-experimental:1.0.0")
 
     androidTestImplementation("androidx.appcompat:appcompat:1.1.0", {
diff --git a/gradlew b/gradlew
index ded855b..3b76e03 100755
--- a/gradlew
+++ b/gradlew
@@ -222,6 +222,41 @@
   TMPDIR_ARG="-Djava.io.tmpdir=$TMPDIR"
 fi
 
+# expand the "--ci" flag
+compact="--ci"
+expanded="--stacktrace\
+ -Pandroidx.summarizeStderr\
+ -Pandroidx.allWarningsAsErrors\
+ -Pandroidx.coverageEnabled=true\
+ -Pandroidx.enableAffectedModuleDetection\
+ -Pandroidx.validateNoUnrecognizedMessages\
+ -PverifyUpToDate\
+ --no-watch-fs\
+ --no-daemon\
+ --offline"
+# Make a copy of our list of arguments, and iterate through the copy
+for arg in "$@"; do
+  # Remove this argument from our list of arguments.
+  # By the time we've completed this loop, we will have removed the original copy of
+  # each argument, and potentially re-added a new copy or an expansion of each.
+  shift
+  # Determine whether to expand this argument
+  if [ "$arg" == "$compact" ]; then
+    # Add the expansion to our arguments
+    set -- "$@" $expanded
+    echo "gradlew expanded '$compact' into '$expanded'"
+    echo
+    # We avoid re-adding this argument itself back into the list for two reasons:
+    # 1. This argument might not be directly understood by Gradle
+    # 2. We want to enforce that all behaviors enabled by this flag can be toggled independently,
+    # so we don't want it to be easy to inadvertently check for the presence of this flag
+    # specifically
+  else
+    # Add this argument back into our arguments
+    set -- "$@" "$arg"
+  fi
+done
+
 function tryToDiagnosePossibleDaemonFailure() {
   # copy daemon logs
   if [ -n "$GRADLE_USER_HOME" ]; then
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
index 9ee34da..ce12c30e 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
@@ -24,8 +24,9 @@
 import com.google.protobuf.gradle.protoc
 import org.gradle.api.Plugin
 import org.gradle.api.Project
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.attributes.Attribute
 import org.gradle.api.tasks.StopExecutionException
-import org.gradle.api.tasks.TaskProvider
 import org.gradle.kotlin.dsl.apply
 import org.gradle.kotlin.dsl.create
 import org.gradle.kotlin.dsl.dependencies
@@ -36,9 +37,8 @@
  * A plugin which, when present, ensures that intermediate inspector
  * resources are generated at build time
  */
+@Suppress("SyntheticAccessor")
 class InspectionPlugin : Plugin<Project> {
-    lateinit var dexTask: TaskProvider<DexInspectorTask>
-
     // project.register* are marked with @ExperimentalStdlibApi, because they use experimental
     // string.capitalize call.
     @ExperimentalStdlibApi
@@ -46,6 +46,13 @@
         var foundLibraryPlugin = false
         var foundReleaseVariant = false
         val extension = project.extensions.create<InspectionExtension>(EXTENSION_NAME, project)
+
+        val publishInspector = project.configurations.create("publishInspector") {
+            it.isCanBeConsumed = true
+            it.isCanBeResolved = false
+            it.setupInspectorAttribute()
+        }
+
         project.pluginManager.withPlugin("com.android.library") {
             foundLibraryPlugin = true
             val libExtension = project.extensions.getByType(LibraryExtension::class.java)
@@ -55,9 +62,14 @@
                     foundReleaseVariant = true
                     val unzip = project.registerUnzipTask(variant)
                     val shadowJar = project.registerShadowDependenciesTask(variant, unzip)
-                    dexTask = project.registerDexInspectorTask(
+                    val dexTask = project.registerDexInspectorTask(
                         variant, libExtension, extension.name, shadowJar
                     )
+
+                    publishInspector.outgoing.variants {
+                        val configVariant = it.create("inspectorJar")
+                        configVariant.artifact(dexTask)
+                    }
                 }
             }
             libExtension.sourceSets.findByName("main")!!.resources.srcDirs(
@@ -124,21 +136,34 @@
  */
 @ExperimentalStdlibApi
 fun packageInspector(libraryProject: Project, inspectorProject: Project) {
+    val consumeInspector = libraryProject.configurations.create("consumeInspector") {
+        it.setupInspectorAttribute()
+    }
+
+    libraryProject.dependencies {
+        add(consumeInspector.name, inspectorProject)
+    }
+
     generateProguardDetectionFile(libraryProject)
-    inspectorProject.project.plugins.withType(InspectionPlugin::class.java) { inspectionPlugin ->
-        val libExtension = libraryProject.extensions.getByType(LibraryExtension::class.java)
-        libExtension.libraryVariants.all { variant ->
-            inspectorProject.afterEvaluate {
-                variant.packageLibraryProvider.configure { zip ->
-                    val outputFile = inspectionPlugin.dexTask.get().outputFile
-                    zip.from(outputFile)
-                    zip.rename(outputFile.asFile.get().name, "inspector.jar")
-                }
+    val libExtension = libraryProject.extensions.getByType(LibraryExtension::class.java)
+    libExtension.libraryVariants.all { variant ->
+        variant.packageLibraryProvider.configure { zip ->
+            zip.from(consumeInspector)
+            zip.rename {
+                if (it == consumeInspector.asFileTree.singleFile.name) {
+                    "inspector.jar"
+                } else it
             }
         }
     }
 }
 
+private fun Configuration.setupInspectorAttribute() {
+    attributes {
+        it.attribute(Attribute.of("inspector", String::class.java), "inspectorJar")
+    }
+}
+
 @ExperimentalStdlibApi
 private fun generateProguardDetectionFile(libraryProject: Project) {
     val libExtension = libraryProject.extensions.getByType(LibraryExtension::class.java)
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt
index e0469ed..c5deef4 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt
@@ -28,7 +28,8 @@
 import androidx.compose.material.Text
 import androidx.compose.material.TextField
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.stringResource
@@ -63,7 +64,7 @@
     Column(Modifier.fillMaxSize().then(Modifier.padding(8.dp))) {
         Text(text = stringResource(Screen.Profile.resourceId))
         Divider(color = Color.Black)
-        val state = savedInstanceState { "" }
+        val state = rememberSaveable { mutableStateOf("") }
         Box {
             TextField(
                 value = state.value,
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavSingleTopDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavSingleTopDemo.kt
index 20021f3..0cc7bcb 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavSingleTopDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavSingleTopDemo.kt
@@ -23,7 +23,8 @@
 import androidx.compose.material.Text
 import androidx.compose.material.TextField
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
@@ -36,7 +37,7 @@
 @Composable
 fun NavSingleTopDemo() {
     val navController = rememberNavController()
-    val query = savedInstanceState { "" }
+    val query = rememberSaveable { mutableStateOf("") }
     Column(Modifier.fillMaxSize().then(Modifier.padding(8.dp))) {
         TextField(
             value = query.value,
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavWithArgsDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavWithArgsDemo.kt
index 0197207..e5253fb 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavWithArgsDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavWithArgsDemo.kt
@@ -24,7 +24,8 @@
 import androidx.compose.material.Text
 import androidx.compose.material.TextField
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.stringResource
@@ -54,7 +55,7 @@
     Column(Modifier.fillMaxSize().then(Modifier.padding(8.dp))) {
         Text(text = stringResource(Screen.Profile.resourceId))
         Divider(color = Color.Black)
-        val state = savedInstanceState { "" }
+        val state = rememberSaveable { mutableStateOf("") }
         Box {
             TextField(
                 value = state.value,
diff --git a/navigation/navigation-ui/api/current.ignore b/navigation/navigation-ui/api/current.ignore
new file mode 100644
index 0000000..ab1aa64
--- /dev/null
+++ b/navigation/navigation-ui/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+ChangedType: androidx.navigation.ui.AppBarConfiguration#getTopLevelDestinations():
+    Method androidx.navigation.ui.AppBarConfiguration.getTopLevelDestinations has changed return type from java.util.Set<java.lang.Integer!> to java.util.Set<java.lang.Integer>
diff --git a/navigation/navigation-ui/api/current.txt b/navigation/navigation-ui/api/current.txt
index 6c7ec57..771ac62 100644
--- a/navigation/navigation-ui/api/current.txt
+++ b/navigation/navigation-ui/api/current.txt
@@ -5,39 +5,44 @@
     method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
     method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
     method public androidx.customview.widget.Openable? getOpenableLayout();
-    method public java.util.Set<java.lang.Integer!> getTopLevelDestinations();
+    method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+    property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+    property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+    property public final androidx.customview.widget.Openable? openableLayout;
+    property public final java.util.Set<java.lang.Integer> topLevelDestinations;
   }
 
   public static final class AppBarConfiguration.Builder {
-    ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph);
-    ctor public AppBarConfiguration.Builder(android.view.Menu);
-    ctor public AppBarConfiguration.Builder(int...);
-    ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer!>);
+    ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+    ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+    ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+    ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
     method public androidx.navigation.ui.AppBarConfiguration build();
-    method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout?);
-    method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener?);
-    method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable?);
+    method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
   }
 
-  public static interface AppBarConfiguration.OnNavigateUpListener {
+  public static fun interface AppBarConfiguration.OnNavigateUpListener {
     method public boolean onNavigateUp();
   }
 
   public final class NavigationUI {
-    method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController);
-    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController);
-    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
-    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
-    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController);
-    method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView, androidx.navigation.NavController);
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView bottomNavigationView, androidx.navigation.NavController navController);
+    field public static final androidx.navigation.ui.NavigationUI INSTANCE;
   }
 
 }
diff --git a/navigation/navigation-ui/api/public_plus_experimental_current.txt b/navigation/navigation-ui/api/public_plus_experimental_current.txt
index 6c7ec57..29b9020e 100644
--- a/navigation/navigation-ui/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-ui/api/public_plus_experimental_current.txt
@@ -5,39 +5,48 @@
     method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
     method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
     method public androidx.customview.widget.Openable? getOpenableLayout();
-    method public java.util.Set<java.lang.Integer!> getTopLevelDestinations();
+    method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+    property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+    property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+    property public final androidx.customview.widget.Openable? openableLayout;
+    property public final java.util.Set<java.lang.Integer> topLevelDestinations;
   }
 
   public static final class AppBarConfiguration.Builder {
-    ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph);
-    ctor public AppBarConfiguration.Builder(android.view.Menu);
-    ctor public AppBarConfiguration.Builder(int...);
-    ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer!>);
+    ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+    ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+    ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+    ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
     method public androidx.navigation.ui.AppBarConfiguration build();
-    method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout?);
-    method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener?);
-    method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable?);
+    method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
   }
 
-  public static interface AppBarConfiguration.OnNavigateUpListener {
+  public static fun interface AppBarConfiguration.OnNavigateUpListener {
     method public boolean onNavigateUp();
   }
 
   public final class NavigationUI {
-    method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController);
-    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController);
-    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
-    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
-    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController);
-    method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView, androidx.navigation.NavController);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static com.google.android.material.bottomsheet.BottomSheetBehavior<?>? findBottomSheetBehavior(android.view.View view);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph graph);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static boolean matchDestination(androidx.navigation.NavDestination destination, @IdRes int destId);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static boolean matchDestinations(androidx.navigation.NavDestination destination, java.util.Set<java.lang.Integer> destinationIds);
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView bottomNavigationView, androidx.navigation.NavController navController);
+    field public static final androidx.navigation.ui.NavigationUI INSTANCE;
   }
 
 }
diff --git a/navigation/navigation-ui/api/restricted_current.ignore b/navigation/navigation-ui/api/restricted_current.ignore
new file mode 100644
index 0000000..ab1aa64
--- /dev/null
+++ b/navigation/navigation-ui/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+ChangedType: androidx.navigation.ui.AppBarConfiguration#getTopLevelDestinations():
+    Method androidx.navigation.ui.AppBarConfiguration.getTopLevelDestinations has changed return type from java.util.Set<java.lang.Integer!> to java.util.Set<java.lang.Integer>
diff --git a/navigation/navigation-ui/api/restricted_current.txt b/navigation/navigation-ui/api/restricted_current.txt
index 6c7ec57..771ac62 100644
--- a/navigation/navigation-ui/api/restricted_current.txt
+++ b/navigation/navigation-ui/api/restricted_current.txt
@@ -5,39 +5,44 @@
     method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
     method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
     method public androidx.customview.widget.Openable? getOpenableLayout();
-    method public java.util.Set<java.lang.Integer!> getTopLevelDestinations();
+    method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+    property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+    property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+    property public final androidx.customview.widget.Openable? openableLayout;
+    property public final java.util.Set<java.lang.Integer> topLevelDestinations;
   }
 
   public static final class AppBarConfiguration.Builder {
-    ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph);
-    ctor public AppBarConfiguration.Builder(android.view.Menu);
-    ctor public AppBarConfiguration.Builder(int...);
-    ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer!>);
+    ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+    ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+    ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+    ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
     method public androidx.navigation.ui.AppBarConfiguration build();
-    method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout?);
-    method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener?);
-    method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable?);
+    method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
   }
 
-  public static interface AppBarConfiguration.OnNavigateUpListener {
+  public static fun interface AppBarConfiguration.OnNavigateUpListener {
     method public boolean onNavigateUp();
   }
 
   public final class NavigationUI {
-    method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController);
-    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController);
-    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
-    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
-    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
-    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
-    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController);
-    method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView, androidx.navigation.NavController);
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView bottomNavigationView, androidx.navigation.NavController navController);
+    field public static final androidx.navigation.ui.NavigationUI INSTANCE;
   }
 
 }
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.java b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.java
deleted file mode 100644
index 59518aa..0000000
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.navigation.ui;
-
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.StringRes;
-import androidx.appcompat.graphics.drawable.DrawerArrowDrawable;
-import androidx.customview.widget.Openable;
-import androidx.navigation.FloatingWindow;
-import androidx.navigation.NavController;
-import androidx.navigation.NavDestination;
-
-import java.lang.ref.WeakReference;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * The abstract OnDestinationChangedListener for keeping any type of app bar updated.
- * This handles both updating the title and updating the Up Indicator, transitioning between
- * the drawer icon and up arrow as needed.
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-abstract class AbstractAppBarOnDestinationChangedListener
-        implements NavController.OnDestinationChangedListener {
-    private final Context mContext;
-    private final Set<Integer> mTopLevelDestinations;
-    @Nullable
-    private final WeakReference<Openable> mOpenableLayoutWeakReference;
-    private DrawerArrowDrawable mArrowDrawable;
-    private ValueAnimator mAnimator;
-
-    AbstractAppBarOnDestinationChangedListener(@NonNull Context context,
-            @NonNull AppBarConfiguration configuration) {
-        mContext = context;
-        mTopLevelDestinations = configuration.getTopLevelDestinations();
-        Openable openableLayout = configuration.getOpenableLayout();
-        if (openableLayout != null) {
-            mOpenableLayoutWeakReference = new WeakReference<>(openableLayout);
-        } else {
-            mOpenableLayoutWeakReference = null;
-        }
-    }
-
-    protected abstract void setTitle(CharSequence title);
-
-    protected abstract void setNavigationIcon(Drawable icon, @StringRes int contentDescription);
-
-    @Override
-    public void onDestinationChanged(@NonNull NavController controller,
-            @NonNull NavDestination destination, @Nullable Bundle arguments) {
-        if (destination instanceof FloatingWindow) {
-            return;
-        }
-        Openable openableLayout = mOpenableLayoutWeakReference != null
-                ? mOpenableLayoutWeakReference.get()
-                : null;
-        if (mOpenableLayoutWeakReference != null && openableLayout == null) {
-            controller.removeOnDestinationChangedListener(this);
-            return;
-        }
-        CharSequence label = destination.getLabel();
-        if (label != null) {
-            // Fill in the data pattern with the args to build a valid URI
-            StringBuffer title = new StringBuffer();
-            Pattern fillInPattern = Pattern.compile("\\{(.+?)\\}");
-            Matcher matcher = fillInPattern.matcher(label);
-            while (matcher.find()) {
-                String argName = matcher.group(1);
-                if (arguments != null && arguments.containsKey(argName)) {
-                    matcher.appendReplacement(title, "");
-                    //noinspection ConstantConditions
-                    title.append(arguments.get(argName).toString());
-                } else {
-                    throw new IllegalArgumentException("Could not find " + argName + " in "
-                            + arguments + " to fill label " + label);
-                }
-            }
-            matcher.appendTail(title);
-            setTitle(title);
-        }
-        boolean isTopLevelDestination = NavigationUI.matchDestinations(destination,
-                mTopLevelDestinations);
-        if (openableLayout == null && isTopLevelDestination) {
-            setNavigationIcon(null, 0);
-        } else {
-            setActionBarUpIndicator(openableLayout != null && isTopLevelDestination);
-        }
-    }
-
-    private void setActionBarUpIndicator(boolean showAsDrawerIndicator) {
-        boolean animate = true;
-        if (mArrowDrawable == null) {
-            mArrowDrawable = new DrawerArrowDrawable(mContext);
-            // We're setting the initial state, so skip the animation
-            animate = false;
-        }
-        setNavigationIcon(mArrowDrawable, showAsDrawerIndicator
-                ? R.string.nav_app_bar_open_drawer_description
-                : R.string.nav_app_bar_navigate_up_description);
-        float endValue = showAsDrawerIndicator ? 0f : 1f;
-        if (animate) {
-            float startValue = mArrowDrawable.getProgress();
-            if (mAnimator != null) {
-                mAnimator.cancel();
-            }
-            mAnimator = ObjectAnimator.ofFloat(mArrowDrawable, "progress",
-                    startValue, endValue);
-            mAnimator.start();
-        } else {
-            mArrowDrawable.setProgress(endValue);
-        }
-    }
-}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.kt
new file mode 100644
index 0000000..4886464
--- /dev/null
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.navigation.ui
+
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import androidx.annotation.RestrictTo
+import androidx.annotation.StringRes
+import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
+import androidx.navigation.FloatingWindow
+import androidx.navigation.NavController
+import androidx.navigation.NavDestination
+import java.lang.ref.WeakReference
+import java.util.regex.Pattern
+
+/**
+ * The abstract OnDestinationChangedListener for keeping any type of app bar updated.
+ * This handles both updating the title and updating the Up Indicator, transitioning between
+ * the drawer icon and up arrow as needed.
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal abstract class AbstractAppBarOnDestinationChangedListener(
+    private val context: Context,
+    configuration: AppBarConfiguration
+) : NavController.OnDestinationChangedListener {
+    private val topLevelDestinations: Set<Int> = configuration.topLevelDestinations
+    private val openableLayoutWeakReference = configuration.openableLayout?.run {
+        WeakReference(this)
+    }
+    private var arrowDrawable: DrawerArrowDrawable? = null
+    private var animator: ValueAnimator? = null
+
+    protected abstract fun setTitle(title: CharSequence?)
+
+    protected abstract fun setNavigationIcon(icon: Drawable?, @StringRes contentDescription: Int)
+
+    override fun onDestinationChanged(
+        controller: NavController,
+        destination: NavDestination,
+        arguments: Bundle?
+    ) {
+        if (destination is FloatingWindow) {
+            return
+        }
+        val openableLayout = openableLayoutWeakReference?.get()
+        if (openableLayoutWeakReference != null && openableLayout == null) {
+            controller.removeOnDestinationChangedListener(this)
+            return
+        }
+        val label = destination.label
+        if (label != null) {
+            // Fill in the data pattern with the args to build a valid URI
+            val title = StringBuffer()
+            val fillInPattern = Pattern.compile("\\{(.+?)\\}")
+            val matcher = fillInPattern.matcher(label)
+            while (matcher.find()) {
+                val argName = matcher.group(1)
+                if (arguments != null && arguments.containsKey(argName)) {
+                    matcher.appendReplacement(title, "")
+                    title.append(arguments[argName].toString())
+                } else {
+                    throw IllegalArgumentException(
+                        "Could not find $argName in $arguments to fill label $label"
+                    )
+                }
+            }
+            matcher.appendTail(title)
+            setTitle(title)
+        }
+        val isTopLevelDestination = NavigationUI.matchDestinations(
+            destination,
+            topLevelDestinations
+        )
+        if (openableLayout == null && isTopLevelDestination) {
+            setNavigationIcon(null, 0)
+        } else {
+            setActionBarUpIndicator(openableLayout != null && isTopLevelDestination)
+        }
+    }
+
+    @SuppressLint("ObjectAnimatorBinding")
+    private fun setActionBarUpIndicator(showAsDrawerIndicator: Boolean) {
+        val (arrow, animate) = arrowDrawable?.run {
+            this to true
+        } ?: DrawerArrowDrawable(context).also { arrowDrawable = it } to false
+
+        setNavigationIcon(
+            arrow,
+            if (showAsDrawerIndicator) R.string.nav_app_bar_open_drawer_description
+            else R.string.nav_app_bar_navigate_up_description
+        )
+
+        val endValue = if (showAsDrawerIndicator) 0f else 1f
+        if (animate) {
+            val startValue = arrow.progress
+            animator?.cancel()
+            animator = ObjectAnimator.ofFloat(arrow, "progress", startValue, endValue)
+            (animator as ObjectAnimator).start()
+        } else {
+            arrow.progress = endValue
+        }
+    }
+}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.kt
index c23f619..6d1d8ad 100644
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.kt
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.kt
@@ -34,7 +34,7 @@
     activity.drawerToggleDelegate!!.actionBarThemedContext,
     configuration
 ) {
-    override fun setTitle(title: CharSequence) {
+    override fun setTitle(title: CharSequence?) {
         val actionBar = activity.supportActionBar
         require(actionBar != null) {
             "Activity $activity does not have an ActionBar set via setSupportActionBar()"
@@ -42,7 +42,7 @@
         actionBar.setTitle(title)
     }
 
-    override fun setNavigationIcon(icon: Drawable, @StringRes contentDescription: Int) {
+    override fun setNavigationIcon(icon: Drawable?, @StringRes contentDescription: Int) {
         val actionBar = activity.supportActionBar
         require(actionBar != null) {
             "Activity $activity does not have an ActionBar set via setSupportActionBar()"
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.java b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.java
deleted file mode 100644
index 8117495..0000000
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.navigation.ui;
-
-import android.annotation.SuppressLint;
-import android.view.Menu;
-import android.view.MenuItem;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.customview.widget.Openable;
-import androidx.drawerlayout.widget.DrawerLayout;
-import androidx.navigation.NavGraph;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Configuration options for {@link NavigationUI} methods that interact with implementations of the
- * app bar pattern such as {@link androidx.appcompat.widget.Toolbar},
- * {@link com.google.android.material.appbar.CollapsingToolbarLayout}, and
- * {@link androidx.appcompat.app.ActionBar}.
- */
-public final class AppBarConfiguration {
-    /**
-     * Interface for providing custom 'up' behavior beyond what is provided by
-     * {@link androidx.navigation.NavController#navigateUp()}.
-     *
-     * @see Builder#setFallbackOnNavigateUpListener(OnNavigateUpListener)
-     * @see NavigationUI#navigateUp(androidx.navigation.NavController, AppBarConfiguration)
-     */
-    public interface OnNavigateUpListener {
-        /**
-         * Callback for handling the Up button.
-         *
-         * @return true if the listener successfully navigated 'up'
-         */
-        boolean onNavigateUp();
-    }
-
-    @NonNull
-    private final Set<Integer> mTopLevelDestinations;
-    @Nullable
-    private final Openable mOpenableLayout;
-    @Nullable
-    private final OnNavigateUpListener mFallbackOnNavigateUpListener;
-
-    private AppBarConfiguration(@NonNull Set<Integer> topLevelDestinations,
-            @Nullable Openable openableLayout,
-            @Nullable OnNavigateUpListener fallbackOnNavigateUpListener) {
-        mTopLevelDestinations = topLevelDestinations;
-        mOpenableLayout = openableLayout;
-        mFallbackOnNavigateUpListener = fallbackOnNavigateUpListener;
-    }
-
-    /**
-     * The set of destinations by id considered at the top level of your information hierarchy.
-     * The Up button will not be displayed when on these destinations.
-     *
-     * @return The set of top level destinations by id.
-     */
-    @NonNull
-    public Set<Integer> getTopLevelDestinations() {
-        return mTopLevelDestinations;
-    }
-
-    /**
-     * The {@link Openable} layout indicating that the Navigation button should be displayed as
-     * a drawer symbol when it is not being shown as an Up button.
-     * @return The Openable layout that should be toggled from the Navigation button
-     */
-    @Nullable
-    public Openable getOpenableLayout() {
-        return mOpenableLayout;
-    }
-
-    /**
-     * The {@link DrawerLayout} indicating that the Navigation button should be displayed as
-     * a drawer symbol when it is not being shown as an Up button.
-     * @return The DrawerLayout that should be toggled from the Navigation button
-     * @deprecated Use {@link #getOpenableLayout()}.
-     */
-    @Deprecated
-    @Nullable
-    public DrawerLayout getDrawerLayout() {
-        if (mOpenableLayout instanceof DrawerLayout) {
-            return (DrawerLayout) mOpenableLayout;
-        }
-        return null;
-    }
-
-    /**
-     * The {@link OnNavigateUpListener} that should be invoked if
-     * {@link androidx.navigation.NavController#navigateUp} returns <code>false</code>.
-     * @return a {@link OnNavigateUpListener} for providing custom up navigation logic,
-     * if one was set.
-     */
-    @Nullable
-    public OnNavigateUpListener getFallbackOnNavigateUpListener() {
-        return mFallbackOnNavigateUpListener;
-    }
-
-    /**
-     * The Builder class for constructing new {@link AppBarConfiguration} instances.
-     */
-    public static final class Builder {
-        @NonNull
-        private final Set<Integer> mTopLevelDestinations = new HashSet<>();
-
-        @Nullable
-        private Openable mOpenableLayout;
-
-        @Nullable
-        private OnNavigateUpListener mFallbackOnNavigateUpListener;
-
-        /**
-         * Create a new Builder whose only top level destination is the start destination
-         * of the given {@link NavGraph}. The Up button will not be displayed when on the
-         * start destination of the graph.
-         *
-         * @param navGraph The NavGraph whose start destination should be considered the only
-         *                 top level destination. The Up button will not be displayed when on the
-         *                 start destination of the graph.
-         */
-        public Builder(@NonNull NavGraph navGraph) {
-            mTopLevelDestinations.add(NavigationUI.findStartDestination(navGraph).getId());
-        }
-
-        /**
-         * Create a new Builder using a {@link Menu} containing all top level destinations. It is
-         * expected that the {@link MenuItem#getItemId() menu item id} of each item corresponds
-         * with a destination in your navigation graph. The Up button will not be displayed when
-         * on these destinations.
-         *
-         * @param topLevelMenu A Menu containing MenuItems corresponding with the destinations
-         *                     considered at the top level of your information hierarchy.
-         *                     The Up button will not be displayed when on these destinations.
-         */
-        public Builder(@NonNull Menu topLevelMenu) {
-            int size = topLevelMenu.size();
-            for (int index = 0; index < size; index++) {
-                MenuItem item = topLevelMenu.getItem(index);
-                mTopLevelDestinations.add(item.getItemId());
-            }
-        }
-
-        /**
-         * Create a new Builder with a specific set of top level destinations. The Up button will
-         * not be displayed when on these destinations.
-         *
-         * @param topLevelDestinationIds The set of destinations by id considered at the top level
-         *                               of your information hierarchy. The Up button will not be
-         *                               displayed when on these destinations.
-         */
-        public Builder(@NonNull int... topLevelDestinationIds) {
-            for (int destinationId : topLevelDestinationIds) {
-                mTopLevelDestinations.add(destinationId);
-            }
-        }
-
-        /**
-         * Create a new Builder with a specific set of top level destinations. The Up button will
-         * not be displayed when on these destinations.
-         *
-         * @param topLevelDestinationIds The set of destinations by id considered at the top level
-         *                               of your information hierarchy. The Up button will not be
-         *                               displayed when on these destinations.
-         */
-        public Builder(@NonNull Set<Integer> topLevelDestinationIds) {
-            mTopLevelDestinations.addAll(topLevelDestinationIds);
-        }
-
-        /**
-         * Display the Navigation button as a drawer symbol when it is not being shown as an
-         * Up button.
-         * @param drawerLayout The DrawerLayout that should be toggled from the Navigation button
-         * @return this {@link Builder}
-         * @deprecated Use {@link #setOpenableLayout(Openable)}.
-         */
-        @Deprecated
-        @NonNull
-        public Builder setDrawerLayout(@Nullable DrawerLayout drawerLayout) {
-            mOpenableLayout = drawerLayout;
-            return this;
-        }
-
-        /**
-         * Display the Navigation button as a drawer symbol when it is not being shown as an
-         * Up button.
-         * @param openableLayout The Openable layout that should be toggled from the Navigation
-         *                       button
-         * @return this {@link Builder}
-         */
-        @NonNull
-        public Builder setOpenableLayout(@Nullable Openable openableLayout) {
-            mOpenableLayout = openableLayout;
-            return this;
-        }
-
-        /**
-         * Adds a {@link OnNavigateUpListener} that will be called as a fallback if the default
-         * behavior of {@link androidx.navigation.NavController#navigateUp}
-         * returns <code>false</code>.
-         *
-         * @param fallbackOnNavigateUpListener Listener that will be invoked if
-         *                                     {@link androidx.navigation.NavController#navigateUp}
-         *                                     returns <code>false</code>.
-         * @return this {@link Builder}
-         */
-        @NonNull
-        public Builder setFallbackOnNavigateUpListener(
-                @Nullable OnNavigateUpListener fallbackOnNavigateUpListener) {
-            mFallbackOnNavigateUpListener = fallbackOnNavigateUpListener;
-            return this;
-        }
-
-        /**
-         * Construct the {@link AppBarConfiguration} instance.
-         *
-         * @return a valid {@link AppBarConfiguration}
-         */
-        @SuppressLint("SyntheticAccessor") /* new AppBarConfiguration() must be private to avoid
-                                              conflicting with the public AppBarConfiguration.kt */
-        @NonNull
-        public AppBarConfiguration build() {
-            return new AppBarConfiguration(mTopLevelDestinations, mOpenableLayout,
-                    mFallbackOnNavigateUpListener);
-        }
-    }
-}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.kt
new file mode 100644
index 0000000..d9ed493
--- /dev/null
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.navigation.ui
+
+import android.annotation.SuppressLint
+import android.view.Menu
+import androidx.customview.widget.Openable
+import androidx.drawerlayout.widget.DrawerLayout
+import androidx.navigation.NavGraph
+import androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener
+import java.util.HashSet
+
+/**
+ * Configuration options for [NavigationUI] methods that interact with implementations of the
+ * app bar pattern such as [androidx.appcompat.widget.Toolbar],
+ * [com.google.android.material.appbar.CollapsingToolbarLayout], and
+ * [androidx.appcompat.app.ActionBar].
+ */
+public class AppBarConfiguration private constructor(
+    /**
+     * The set of destinations by id considered at the top level of your information hierarchy.
+     * The Up button will not be displayed when on these destinations.
+     *
+     * @return The set of top level destinations by id.
+     */
+    public val topLevelDestinations: Set<Int>,
+    /**
+     * The [Openable] layout indicating that the Navigation button should be displayed as
+     * a drawer symbol when it is not being shown as an Up button.
+     * @return The Openable layout that should be toggled from the Navigation button
+     */
+    public val openableLayout: Openable?,
+    /**
+     * The [OnNavigateUpListener] that should be invoked if
+     * [androidx.navigation.NavController.navigateUp] returns `false`.
+     * @return a [OnNavigateUpListener] for providing custom up navigation logic,
+     * if one was set.
+     */
+    public val fallbackOnNavigateUpListener: OnNavigateUpListener?
+) {
+    /**
+     * Interface for providing custom 'up' behavior beyond what is provided by
+     * [androidx.navigation.NavController.navigateUp].
+     *
+     * @see Builder.setFallbackOnNavigateUpListener
+     * @see NavigationUI.navigateUp
+     */
+    public fun interface OnNavigateUpListener {
+        /**
+         * Callback for handling the Up button.
+         *
+         * @return true if the listener successfully navigated 'up'
+         */
+        public fun onNavigateUp(): Boolean
+    }
+
+    /**
+     * The [DrawerLayout] indicating that the Navigation button should be displayed as
+     * a drawer symbol when it is not being shown as an Up button.
+     * @return The DrawerLayout that should be toggled from the Navigation button
+     */
+    @get:Deprecated("Use {@link #getOpenableLayout()}.")
+    public val drawerLayout: DrawerLayout?
+        get() = if (openableLayout is DrawerLayout) {
+            openableLayout
+        } else null
+
+    /**
+     * The Builder class for constructing new [AppBarConfiguration] instances.
+     */
+    public class Builder {
+        private val topLevelDestinations: MutableSet<Int> = HashSet()
+        private var openableLayout: Openable? = null
+        private var fallbackOnNavigateUpListener: OnNavigateUpListener? = null
+
+        /**
+         * Create a new Builder whose only top level destination is the start destination
+         * of the given [NavGraph]. The Up button will not be displayed when on the
+         * start destination of the graph.
+         *
+         * @param navGraph The NavGraph whose start destination should be considered the only
+         * top level destination. The Up button will not be displayed when on the
+         * start destination of the graph.
+         */
+        public constructor(navGraph: NavGraph) {
+            topLevelDestinations.add(NavigationUI.findStartDestination(navGraph).id)
+        }
+
+        /**
+         * Create a new Builder using a [Menu] containing all top level destinations. It is
+         * expected that the [menu item id][MenuItem.getItemId] of each item corresponds
+         * with a destination in your navigation graph. The Up button will not be displayed when
+         * on these destinations.
+         *
+         * @param topLevelMenu A Menu containing MenuItems corresponding with the destinations
+         * considered at the top level of your information hierarchy.
+         * The Up button will not be displayed when on these destinations.
+         */
+        public constructor(topLevelMenu: Menu) {
+            val size = topLevelMenu.size()
+            for (index in 0 until size) {
+                val item = topLevelMenu.getItem(index)
+                topLevelDestinations.add(item.itemId)
+            }
+        }
+
+        /**
+         * Create a new Builder with a specific set of top level destinations. The Up button will
+         * not be displayed when on these destinations.
+         *
+         * @param topLevelDestinationIds The set of destinations by id considered at the top level
+         * of your information hierarchy. The Up button will not be
+         * displayed when on these destinations.
+         */
+        public constructor(vararg topLevelDestinationIds: Int) {
+            for (destinationId in topLevelDestinationIds) {
+                topLevelDestinations.add(destinationId)
+            }
+        }
+
+        /**
+         * Create a new Builder with a specific set of top level destinations. The Up button will
+         * not be displayed when on these destinations.
+         *
+         * @param topLevelDestinationIds The set of destinations by id considered at the top level
+         * of your information hierarchy. The Up button will not be
+         * displayed when on these destinations.
+         */
+        public constructor(topLevelDestinationIds: Set<Int>) {
+            topLevelDestinations.addAll(topLevelDestinationIds)
+        }
+
+        /**
+         * Display the Navigation button as a drawer symbol when it is not being shown as an
+         * Up button.
+         * @param drawerLayout The DrawerLayout that should be toggled from the Navigation button
+         * @return this [Builder]
+         */
+        @Deprecated("Use {@link #setOpenableLayout(Openable)}.")
+        public fun setDrawerLayout(drawerLayout: DrawerLayout?): Builder {
+            openableLayout = drawerLayout
+            return this
+        }
+
+        /**
+         * Display the Navigation button as a drawer symbol when it is not being shown as an
+         * Up button.
+         * @param openableLayout The Openable layout that should be toggled from the Navigation
+         * button
+         * @return this [Builder]
+         */
+        public fun setOpenableLayout(openableLayout: Openable?): Builder {
+            this.openableLayout = openableLayout
+            return this
+        }
+
+        /**
+         * Adds a [OnNavigateUpListener] that will be called as a fallback if the default
+         * behavior of [androidx.navigation.NavController.navigateUp]
+         * returns `false`.
+         *
+         * @param fallbackOnNavigateUpListener Listener that will be invoked if
+         * [androidx.navigation.NavController.navigateUp]
+         * returns `false`.
+         * @return this [Builder]
+         */
+        public fun setFallbackOnNavigateUpListener(
+            fallbackOnNavigateUpListener: OnNavigateUpListener?
+        ): Builder {
+            this.fallbackOnNavigateUpListener = fallbackOnNavigateUpListener
+            return this
+        }
+
+        /**
+         * Construct the [AppBarConfiguration] instance.
+         *
+         * @return a valid [AppBarConfiguration]
+         */
+        @SuppressLint("SyntheticAccessor") /* new AppBarConfiguration() must be private to avoid
+                                              conflicting with the public AppBarConfiguration.kt */
+        public fun build(): AppBarConfiguration {
+            return AppBarConfiguration(
+                topLevelDestinations,
+                openableLayout,
+                fallbackOnNavigateUpListener
+            )
+        }
+    }
+}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/CollapsingToolbarOnDestinationChangedListener.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/CollapsingToolbarOnDestinationChangedListener.kt
index faa449d..f878edb 100644
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/CollapsingToolbarOnDestinationChangedListener.kt
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/CollapsingToolbarOnDestinationChangedListener.kt
@@ -58,7 +58,7 @@
         super.onDestinationChanged(controller, destination, arguments)
     }
 
-    override fun setTitle(title: CharSequence) {
+    override fun setTitle(title: CharSequence?) {
         val collapsingToolbarLayout = mCollapsingToolbarLayoutWeakReference.get()
         if (collapsingToolbarLayout != null) {
             collapsingToolbarLayout.title = title
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.java b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.java
deleted file mode 100644
index 4609bef..0000000
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.java
+++ /dev/null
@@ -1,603 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.navigation.ui;
-
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-import androidx.annotation.IdRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import androidx.customview.widget.Openable;
-import androidx.navigation.ActivityNavigator;
-import androidx.navigation.NavController;
-import androidx.navigation.NavDestination;
-import androidx.navigation.NavGraph;
-import androidx.navigation.NavOptions;
-
-import com.google.android.material.appbar.CollapsingToolbarLayout;
-import com.google.android.material.bottomnavigation.BottomNavigationView;
-import com.google.android.material.bottomsheet.BottomSheetBehavior;
-import com.google.android.material.navigation.NavigationView;
-
-import java.lang.ref.WeakReference;
-import java.util.Set;
-
-/**
- * Class which hooks up elements typically in the 'chrome' of your application such as global
- * navigation patterns like a navigation drawer or bottom nav bar with your {@link NavController}.
- */
-public final class NavigationUI {
-
-    // No instances. Static utilities only.
-    private NavigationUI() {
-    }
-
-    /**
-     * Attempt to navigate to the {@link NavDestination} associated with the given MenuItem. This
-     * MenuItem should have been added via one of the helper methods in this class.
-     *
-     * <p>Importantly, it assumes the {@link MenuItem#getItemId() menu item id} matches a valid
-     * {@link NavDestination#getAction(int) action id} or
-     * {@link NavDestination#getId() destination id} to be navigated to.</p>
-     * <p>
-     * By default, the back stack will be popped back to the navigation graph's start destination.
-     * Menu items that have <code>android:menuCategory="secondary"</code> will not pop the back
-     * stack.
-     *
-     * @param item The selected MenuItem.
-     * @param navController The NavController that hosts the destination.
-     * @return True if the {@link NavController} was able to navigate to the destination
-     * associated with the given MenuItem.
-     */
-    public static boolean onNavDestinationSelected(@NonNull MenuItem item,
-            @NonNull NavController navController) {
-        NavOptions.Builder builder = new NavOptions.Builder()
-                .setLaunchSingleTop(true);
-        if (navController.getCurrentDestination().getParent().findNode(item.getItemId())
-                instanceof ActivityNavigator.Destination) {
-            builder.setEnterAnim(R.anim.nav_default_enter_anim)
-                    .setExitAnim(R.anim.nav_default_exit_anim)
-                    .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
-                    .setPopExitAnim(R.anim.nav_default_pop_exit_anim);
-
-        } else {
-            builder.setEnterAnim(R.animator.nav_default_enter_anim)
-                    .setExitAnim(R.animator.nav_default_exit_anim)
-                    .setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
-                    .setPopExitAnim(R.animator.nav_default_pop_exit_anim);
-        }
-        if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
-            builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
-        }
-        NavOptions options = builder.build();
-        try {
-            //TODO provide proper API instead of using Exceptions as Control-Flow.
-            navController.navigate(item.getItemId(), null, options);
-            return true;
-        } catch (IllegalArgumentException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Handles the Up button by delegating its behavior to the given NavController. This should
-     * generally be called from {@link AppCompatActivity#onSupportNavigateUp()}.
-     * <p>If you do not have a {@link Openable} layout, you should call
-     * {@link NavController#navigateUp()} directly.
-     *
-     * @param navController The NavController that hosts your content.
-     * @param openableLayout The Openable layout that should be opened if you are on the topmost
-     *                       level of the app.
-     * @return True if the {@link NavController} was able to navigate up.
-     */
-    public static boolean navigateUp(@NonNull NavController navController,
-            @Nullable Openable openableLayout) {
-        return navigateUp(navController, new AppBarConfiguration.Builder(navController.getGraph())
-                .setOpenableLayout(openableLayout)
-                .build());
-    }
-
-    /**
-     * Handles the Up button by delegating its behavior to the given NavController. This is
-     * an alternative to using {@link NavController#navigateUp()} directly when the given
-     * {@link AppBarConfiguration} needs to be considered when determining what should happen
-     * when the Up button is pressed.
-     * <p>
-     * In cases where no Up action is available, the
-     * {@link AppBarConfiguration#getFallbackOnNavigateUpListener()} will be called to provide
-     * additional control.
-     *
-     * @param navController The NavController that hosts your content.
-     * @param configuration Additional configuration options for determining what should happen
-     *                      when the Up button is pressed.
-     * @return True if the {@link NavController} was able to navigate up.
-     */
-    public static boolean navigateUp(@NonNull NavController navController,
-            @NonNull AppBarConfiguration configuration) {
-        Openable openableLayout = configuration.getOpenableLayout();
-        NavDestination currentDestination = navController.getCurrentDestination();
-        Set<Integer> topLevelDestinations = configuration.getTopLevelDestinations();
-        if (openableLayout != null && currentDestination != null
-                && matchDestinations(currentDestination, topLevelDestinations)) {
-            openableLayout.open();
-            return true;
-        } else {
-            if (navController.navigateUp()) {
-                return true;
-            } else if (configuration.getFallbackOnNavigateUpListener() != null) {
-                return configuration.getFallbackOnNavigateUpListener().onNavigateUp();
-            } else {
-                return false;
-            }
-        }
-    }
-
-    /**
-     * Sets up the ActionBar returned by {@link AppCompatActivity#getSupportActionBar()} for use
-     * with a {@link NavController}.
-     *
-     * <p>By calling this method, the title in the action bar will automatically be updated when
-     * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
-     *
-     * <p>The start destination of your navigation graph is considered the only top level
-     * destination. On all other destinations, the ActionBar will show the Up button.
-     * Call {@link NavController#navigateUp()} to handle the Up button.
-     *
-     * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
-     *
-     * @param activity The activity hosting the action bar that should be kept in sync with changes
-     *                 to the NavController.
-     * @param navController The NavController that supplies the secondary menu. Navigation actions
-     *                      on this NavController will be reflected in the title of the action bar.
-     * @see #setupActionBarWithNavController(AppCompatActivity, NavController, AppBarConfiguration)
-     */
-    public static void setupActionBarWithNavController(@NonNull AppCompatActivity activity,
-            @NonNull NavController navController) {
-        setupActionBarWithNavController(activity, navController,
-                new AppBarConfiguration.Builder(navController.getGraph())
-                        .build());
-    }
-
-    /**
-     * Sets up the ActionBar returned by {@link AppCompatActivity#getSupportActionBar()} for use
-     * with a {@link NavController}.
-     *
-     * <p>By calling this method, the title in the action bar will automatically be updated when
-     * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
-     *
-     * <p>The start destination of your navigation graph is considered the only top level
-     * destination. On the start destination of your navigation graph, the ActionBar will show
-     * the drawer icon if the given Openable layout is non null. On all other destinations,
-     * the ActionBar will show the Up button.
-     * Call {@link #navigateUp(NavController, Openable)} to handle the Up button.
-     *
-     * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
-     *
-     * @param activity The activity hosting the action bar that should be kept in sync with changes
-     *                 to the NavController.
-     * @param navController The NavController whose navigation actions will be reflected
-     *                      in the title of the action bar.
-     * @param openableLayout The Openable layout that should be toggled from the home button
-     * @see #setupActionBarWithNavController(AppCompatActivity, NavController, AppBarConfiguration)
-     */
-    public static void setupActionBarWithNavController(@NonNull AppCompatActivity activity,
-            @NonNull NavController navController,
-            @Nullable Openable openableLayout) {
-        setupActionBarWithNavController(activity, navController,
-                new AppBarConfiguration.Builder(navController.getGraph())
-                        .setOpenableLayout(openableLayout)
-                        .build());
-    }
-
-    /**
-     * Sets up the ActionBar returned by {@link AppCompatActivity#getSupportActionBar()} for use
-     * with a {@link NavController}.
-     *
-     * <p>By calling this method, the title in the action bar will automatically be updated when
-     * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
-     *
-     * <p>The {@link AppBarConfiguration} you provide controls how the Navigation button is
-     * displayed.
-     * Call {@link #navigateUp(NavController, AppBarConfiguration)} to handle the Up button.
-     *
-     * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
-     *
-     *  @param activity The activity hosting the action bar that should be kept in sync with changes
-     *                 to the NavController.
-     * @param navController The NavController whose navigation actions will be reflected
-     *                      in the title of the action bar.
-     * @param configuration Additional configuration options for customizing the behavior of the
-     *                      ActionBar
-     */
-    public static void setupActionBarWithNavController(@NonNull AppCompatActivity activity,
-            @NonNull NavController navController,
-            @NonNull AppBarConfiguration configuration) {
-        navController.addOnDestinationChangedListener(
-                new ActionBarOnDestinationChangedListener(activity, configuration));
-    }
-
-    /**
-     * Sets up a {@link Toolbar} for use with a {@link NavController}.
-     *
-     * <p>By calling this method, the title in the Toolbar will automatically be updated when
-     * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
-     *
-     * <p>The start destination of your navigation graph is considered the only top level
-     * destination. On all other destinations, the Toolbar will show the Up button. This
-     * method will call {@link NavController#navigateUp()} when the Navigation button
-     * is clicked.
-     *
-     * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
-     *
-     * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
-     * @param navController The NavController that supplies the secondary menu. Navigation actions
-     *                      on this NavController will be reflected in the title of the Toolbar.
-     * @see #setupWithNavController(Toolbar, NavController, AppBarConfiguration)
-     */
-    public static void setupWithNavController(@NonNull Toolbar toolbar,
-            @NonNull NavController navController) {
-        setupWithNavController(toolbar, navController,
-                new AppBarConfiguration.Builder(navController.getGraph()).build());
-    }
-
-    /**
-     * Sets up a {@link Toolbar} for use with a {@link NavController}.
-     *
-     * <p>By calling this method, the title in the Toolbar will automatically be updated when
-     * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
-     *
-     * <p>The start destination of your navigation graph is considered the only top level
-     * destination. On the start destination of your navigation graph, the Toolbar will show
-     * the drawer icon if the given Openable layout is non null. On all other destinations,
-     * the Toolbar will show the Up button. This method will call
-     * {@link #navigateUp(NavController, Openable)} when the Navigation button is clicked.
-     *
-     * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
-     *
-     * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
-     * @param navController The NavController whose navigation actions will be reflected
-     *                      in the title of the Toolbar.
-     * @param openableLayout The Openable layout that should be toggled from the Navigation button
-     * @see #setupWithNavController(Toolbar, NavController, AppBarConfiguration)
-     */
-    public static void setupWithNavController(@NonNull Toolbar toolbar,
-            @NonNull final NavController navController,
-            @Nullable final Openable openableLayout) {
-        setupWithNavController(toolbar, navController,
-                new AppBarConfiguration.Builder(navController.getGraph())
-                        .setOpenableLayout(openableLayout)
-                        .build());
-    }
-
-    /**
-     * Sets up a {@link Toolbar} for use with a {@link NavController}.
-     *
-     * <p>By calling this method, the title in the Toolbar will automatically be updated when
-     * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
-     *
-     * <p>The {@link AppBarConfiguration} you provide controls how the Navigation button is
-     * displayed and what action is triggered when the Navigation button is tapped. This method
-     * will call {@link #navigateUp(NavController, AppBarConfiguration)} when the Navigation button
-     * is clicked.
-     *
-     * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
-     *
-     * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
-     * @param navController The NavController whose navigation actions will be reflected
-     *                      in the title of the Toolbar.
-     * @param configuration Additional configuration options for customizing the behavior of the
-     *                      Toolbar
-     */
-    public static void setupWithNavController(@NonNull Toolbar toolbar,
-            @NonNull final NavController navController,
-            @NonNull final AppBarConfiguration configuration) {
-        navController.addOnDestinationChangedListener(
-                new ToolbarOnDestinationChangedListener(toolbar, configuration));
-        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                navigateUp(navController, configuration);
-            }
-        });
-    }
-
-    /**
-     * Sets up a {@link CollapsingToolbarLayout} and {@link Toolbar} for use with a
-     * {@link NavController}.
-     *
-     * <p>By calling this method, the title in the CollapsingToolbarLayout will automatically be
-     * updated when the destination changes (assuming there is a valid
-     * {@link NavDestination#getLabel label}).
-     *
-     * <p>The start destination of your navigation graph is considered the only top level
-     * destination. On all other destinations, the Toolbar will show the Up button. This
-     * method will call {@link NavController#navigateUp()} when the Navigation button
-     * is clicked.
-     *
-     * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
-     *
-     * @param collapsingToolbarLayout The CollapsingToolbarLayout that should be kept in sync with
-     *                                changes to the NavController.
-     * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
-     * @param navController The NavController that supplies the secondary menu. Navigation actions
-     *                      on this NavController will be reflected in the title of the Toolbar.
-     */
-    public static void setupWithNavController(
-            @NonNull CollapsingToolbarLayout collapsingToolbarLayout,
-            @NonNull Toolbar toolbar,
-            @NonNull NavController navController) {
-        setupWithNavController(collapsingToolbarLayout, toolbar, navController,
-                new AppBarConfiguration.Builder(navController.getGraph()).build());
-    }
-
-    /**
-     * Sets up a {@link CollapsingToolbarLayout} and {@link Toolbar} for use with a
-     * {@link NavController}.
-     *
-     * <p>By calling this method, the title in the CollapsingToolbarLayout will automatically be
-     * updated when the destination changes (assuming there is a valid
-     * {@link NavDestination#getLabel label}).
-     *
-     * <p>The start destination of your navigation graph is considered the only top level
-     * destination. On the start destination of your navigation graph, the Toolbar will show
-     * the drawer icon if the given Openable layout is non null. On all other destinations,
-     * the Toolbar will show the Up button. This method will call
-     * {@link #navigateUp(NavController, Openable)} when the Navigation button is clicked.
-     *
-     * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
-     *
-     * @param collapsingToolbarLayout The CollapsingToolbarLayout that should be kept in sync with
-     *                                changes to the NavController.
-     * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
-     * @param navController The NavController whose navigation actions will be reflected
-     *                      in the title of the Toolbar.
-     * @param openableLayout The Openable layout that should be toggled from the Navigation button
-     */
-    public static void setupWithNavController(
-            @NonNull CollapsingToolbarLayout collapsingToolbarLayout,
-            @NonNull Toolbar toolbar,
-            @NonNull final NavController navController,
-            @Nullable final Openable openableLayout) {
-        setupWithNavController(collapsingToolbarLayout, toolbar, navController,
-                new AppBarConfiguration.Builder(navController.getGraph())
-                        .setOpenableLayout(openableLayout)
-                        .build());
-    }
-
-    /**
-     * Sets up a {@link CollapsingToolbarLayout} and {@link Toolbar} for use with a
-     * {@link NavController}.
-     *
-     * <p>By calling this method, the title in the CollapsingToolbarLayout will automatically be
-     * updated when the destination changes (assuming there is a valid
-     * {@link NavDestination#getLabel label}).
-     *
-     * <p>The {@link AppBarConfiguration} you provide controls how the Navigation button is
-     * displayed and what action is triggered when the Navigation button is tapped. This method
-     * will call {@link #navigateUp(NavController, AppBarConfiguration)} when the Navigation button
-     * is clicked.
-     *
-     * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
-     *
-     * @param collapsingToolbarLayout The CollapsingToolbarLayout that should be kept in sync with
-     *                                changes to the NavController.
-     * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
-     * @param navController The NavController whose navigation actions will be reflected
-     *                      in the title of the Toolbar.
-     * @param configuration Additional configuration options for customizing the behavior of the
-     *                      Toolbar
-     */
-    public static void setupWithNavController(
-            @NonNull CollapsingToolbarLayout collapsingToolbarLayout,
-            @NonNull Toolbar toolbar,
-            @NonNull final NavController navController,
-            @NonNull final AppBarConfiguration configuration) {
-        navController.addOnDestinationChangedListener(
-                new CollapsingToolbarOnDestinationChangedListener(
-                        collapsingToolbarLayout, toolbar, configuration));
-        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                navigateUp(navController, configuration);
-            }
-        });
-    }
-
-    /**
-     * Sets up a {@link NavigationView} for use with a {@link NavController}. This will call
-     * {@link #onNavDestinationSelected(MenuItem, NavController)} when a menu item is selected.
-     * The selected item in the NavigationView will automatically be updated when the destination
-     * changes.
-     * <p>
-     * If the {@link NavigationView} is directly contained with an {@link Openable} layout,
-     * it will be closed when a menu item is selected.
-     * <p>
-     * Similarly, if the {@link NavigationView} has a {@link BottomSheetBehavior} associated with
-     * it (as is the case when using a {@link com.google.android.material.bottomsheet.BottomSheetDialog}),
-     * the bottom sheet will be hidden when a menu item is selected.
-     *
-     * @param navigationView The NavigationView that should be kept in sync with changes to the
-     *                       NavController.
-     * @param navController The NavController that supplies the primary and secondary menu.
-     *                      Navigation actions on this NavController will be reflected in the
-     *                      selected item in the NavigationView.
-     */
-    public static void setupWithNavController(@NonNull final NavigationView navigationView,
-            @NonNull final NavController navController) {
-        navigationView.setNavigationItemSelectedListener(
-                new NavigationView.OnNavigationItemSelectedListener() {
-                    @Override
-                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
-                        boolean handled = onNavDestinationSelected(item, navController);
-                        if (handled) {
-                            ViewParent parent = navigationView.getParent();
-                            if (parent instanceof Openable) {
-                                ((Openable) parent).close();
-                            } else {
-                                BottomSheetBehavior bottomSheetBehavior =
-                                        findBottomSheetBehavior(navigationView);
-                                if (bottomSheetBehavior != null) {
-                                    bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
-                                }
-                            }
-                        }
-                        return handled;
-                    }
-                });
-        final WeakReference<NavigationView> weakReference = new WeakReference<>(navigationView);
-        navController.addOnDestinationChangedListener(
-                new NavController.OnDestinationChangedListener() {
-                    @Override
-                    public void onDestinationChanged(@NonNull NavController controller,
-                            @NonNull NavDestination destination, @Nullable Bundle arguments) {
-                        NavigationView view = weakReference.get();
-                        if (view == null) {
-                            navController.removeOnDestinationChangedListener(this);
-                            return;
-                        }
-                        Menu menu = view.getMenu();
-                        for (int h = 0, size = menu.size(); h < size; h++) {
-                            MenuItem item = menu.getItem(h);
-                            item.setChecked(matchDestination(destination, item.getItemId()));
-                        }
-                    }
-                });
-    }
-
-    /**
-     * Walks up the view hierarchy, trying to determine if the given View is contained within
-     * a bottom sheet.
-     */
-    @SuppressWarnings("WeakerAccess")
-    static BottomSheetBehavior findBottomSheetBehavior(@NonNull View view) {
-        ViewGroup.LayoutParams params = view.getLayoutParams();
-        if (!(params instanceof CoordinatorLayout.LayoutParams)) {
-            ViewParent parent = view.getParent();
-            if (parent instanceof View) {
-                return findBottomSheetBehavior((View) parent);
-            }
-            return null;
-        }
-        CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
-                .getBehavior();
-        if (!(behavior instanceof BottomSheetBehavior)) {
-            // We hit a CoordinatorLayout, but the View doesn't have the BottomSheetBehavior
-            return null;
-        }
-        return (BottomSheetBehavior) behavior;
-    }
-
-    /**
-     * Sets up a {@link BottomNavigationView} for use with a {@link NavController}. This will call
-     * {@link #onNavDestinationSelected(MenuItem, NavController)} when a menu item is selected. The
-     * selected item in the BottomNavigationView will automatically be updated when the destination
-     * changes.
-     *
-     * @param bottomNavigationView The BottomNavigationView that should be kept in sync with
-     *                             changes to the NavController.
-     * @param navController The NavController that supplies the primary menu.
-     *                      Navigation actions on this NavController will be reflected in the
-     *                      selected item in the BottomNavigationView.
-     */
-    public static void setupWithNavController(
-            @NonNull final BottomNavigationView bottomNavigationView,
-            @NonNull final NavController navController) {
-        bottomNavigationView.setOnNavigationItemSelectedListener(
-                new BottomNavigationView.OnNavigationItemSelectedListener() {
-                    @Override
-                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
-                        return onNavDestinationSelected(item, navController);
-                    }
-                });
-        final WeakReference<BottomNavigationView> weakReference =
-                new WeakReference<>(bottomNavigationView);
-        navController.addOnDestinationChangedListener(
-                new NavController.OnDestinationChangedListener() {
-                    @Override
-                    public void onDestinationChanged(@NonNull NavController controller,
-                            @NonNull NavDestination destination, @Nullable Bundle arguments) {
-                        BottomNavigationView view = weakReference.get();
-                        if (view == null) {
-                            navController.removeOnDestinationChangedListener(this);
-                            return;
-                        }
-                        Menu menu = view.getMenu();
-                        for (int h = 0, size = menu.size(); h < size; h++) {
-                            MenuItem item = menu.getItem(h);
-                            if (matchDestination(destination, item.getItemId())) {
-                                item.setChecked(true);
-                            }
-                        }
-                    }
-                });
-    }
-
-    /**
-     * Determines whether the given <code>destId</code> matches the NavDestination. This handles
-     * both the default case (the destination's id matches the given id) and the nested case where
-     * the given id is a parent/grandparent/etc of the destination.
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    static boolean matchDestination(@NonNull NavDestination destination,
-            @IdRes int destId) {
-        NavDestination currentDestination = destination;
-        while (currentDestination.getId() != destId && currentDestination.getParent() != null) {
-            currentDestination = currentDestination.getParent();
-        }
-        return currentDestination.getId() == destId;
-    }
-
-    /**
-     * Determines whether the given <code>destinationIds</code> match the NavDestination. This
-     * handles both the default case (the destination's id is in the given ids) and the nested
-     * case where the given ids is a parent/grandparent/etc of the destination.
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    static boolean matchDestinations(@NonNull NavDestination destination,
-            @NonNull Set<Integer> destinationIds) {
-        NavDestination currentDestination = destination;
-        do {
-            if (destinationIds.contains(currentDestination.getId())) {
-                return true;
-            }
-            currentDestination = currentDestination.getParent();
-        } while (currentDestination != null);
-        return false;
-    }
-
-    /**
-     * Finds the actual start destination of the graph, handling cases where the graph's starting
-     * destination is itself a NavGraph.
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    static NavDestination findStartDestination(@NonNull NavGraph graph) {
-        NavDestination startDestination = graph;
-        while (startDestination instanceof NavGraph) {
-            NavGraph parent = (NavGraph) startDestination;
-            startDestination = parent.findNode(parent.getStartDestination());
-        }
-        return startDestination;
-    }
-}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.kt
new file mode 100644
index 0000000..55b453f
--- /dev/null
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.kt
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.navigation.ui
+
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import androidx.annotation.IdRes
+import androidx.annotation.RestrictTo
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.Toolbar
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import androidx.core.view.forEach
+import androidx.customview.widget.Openable
+import androidx.navigation.ActivityNavigator
+import androidx.navigation.NavController
+import androidx.navigation.NavDestination
+import androidx.navigation.NavGraph
+import androidx.navigation.NavOptions
+import com.google.android.material.appbar.CollapsingToolbarLayout
+import com.google.android.material.bottomnavigation.BottomNavigationView
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.navigation.NavigationView
+import java.lang.IllegalArgumentException
+import java.lang.ref.WeakReference
+
+/**
+ * Class which hooks up elements typically in the 'chrome' of your application such as global
+ * navigation patterns like a navigation drawer or bottom nav bar with your [NavController].
+ */
+public object NavigationUI {
+    /**
+     * Attempt to navigate to the [NavDestination] associated with the given MenuItem. This
+     * MenuItem should have been added via one of the helper methods in this class.
+     *
+     * Importantly, it assumes the [menu item id][MenuItem.getItemId] matches a valid
+     * [action id][NavDestination.getAction] or [destination id][NavDestination.getId] to be
+     * navigated to.
+     *
+     * By default, the back stack will be popped back to the navigation graph's start destination.
+     * Menu items that have `android:menuCategory="secondary"` will not pop the back
+     * stack.
+     *
+     * @param item The selected MenuItem.
+     * @param navController The NavController that hosts the destination.
+     * @return True if the [NavController] was able to navigate to the destination
+     * associated with the given MenuItem.
+     */
+    @JvmStatic
+    public fun onNavDestinationSelected(item: MenuItem, navController: NavController): Boolean {
+        val builder = NavOptions.Builder().setLaunchSingleTop(true)
+        if (
+            navController.currentDestination!!.parent!!.findNode(item.itemId)
+            is ActivityNavigator.Destination
+        ) {
+            builder.setEnterAnim(R.anim.nav_default_enter_anim)
+                .setExitAnim(R.anim.nav_default_exit_anim)
+                .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
+                .setPopExitAnim(R.anim.nav_default_pop_exit_anim)
+        } else {
+            builder.setEnterAnim(R.animator.nav_default_enter_anim)
+                .setExitAnim(R.animator.nav_default_exit_anim)
+                .setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
+                .setPopExitAnim(R.animator.nav_default_pop_exit_anim)
+        }
+        if (item.order and Menu.CATEGORY_SECONDARY == 0) {
+            builder.setPopUpTo(findStartDestination(navController.graph).id, false)
+        }
+        val options = builder.build()
+        return try {
+            // TODO provide proper API instead of using Exceptions as Control-Flow.
+            navController.navigate(item.itemId, null, options)
+            true
+        } catch (e: IllegalArgumentException) {
+            false
+        }
+    }
+
+    /**
+     * Handles the Up button by delegating its behavior to the given NavController. This should
+     * generally be called from [AppCompatActivity.onSupportNavigateUp].
+     *
+     * If you do not have a [Openable] layout, you should call
+     * [NavController.navigateUp] directly.
+     *
+     * @param navController The NavController that hosts your content.
+     * @param openableLayout The Openable layout that should be opened if you are on the topmost
+     * level of the app.
+     * @return True if the [NavController] was able to navigate up.
+     */
+    @JvmStatic
+    public fun navigateUp(navController: NavController, openableLayout: Openable?): Boolean =
+        navigateUp(
+            navController,
+            AppBarConfiguration.Builder(navController.graph)
+                .setOpenableLayout(openableLayout)
+                .build()
+        )
+
+    /**
+     * Handles the Up button by delegating its behavior to the given NavController. This is
+     * an alternative to using [NavController.navigateUp] directly when the given
+     * [AppBarConfiguration] needs to be considered when determining what should happen
+     * when the Up button is pressed.
+     *
+     * In cases where no Up action is available, the
+     * [AppBarConfiguration.getFallbackOnNavigateUpListener] will be called to provide
+     * additional control.
+     *
+     * @param navController The NavController that hosts your content.
+     * @param configuration Additional configuration options for determining what should happen
+     * when the Up button is pressed.
+     * @return True if the [NavController] was able to navigate up.
+     */
+    @JvmStatic
+    public fun navigateUp(
+        navController: NavController,
+        configuration: AppBarConfiguration
+    ): Boolean {
+        val openableLayout = configuration.openableLayout
+        val currentDestination = navController.currentDestination
+        val topLevelDestinations = configuration.topLevelDestinations
+        return if (openableLayout != null && currentDestination != null && matchDestinations(
+                currentDestination,
+                topLevelDestinations
+            )
+        ) {
+            openableLayout.open()
+            true
+        } else {
+            return if (navController.navigateUp()) {
+                true
+            } else configuration.fallbackOnNavigateUpListener?.onNavigateUp() ?: false
+        }
+    }
+
+    /**
+     * Sets up the ActionBar returned by [AppCompatActivity.getSupportActionBar] for use
+     * with a [NavController].
+     *
+     * By calling this method, the title in the action bar will automatically be updated when
+     * the destination changes (assuming there is a valid [label][NavDestination.getLabel]).
+     *
+     * The start destination of your navigation graph is considered the only top level
+     * destination. On the start destination of your navigation graph, the ActionBar will show
+     * the drawer icon if the given Openable layout is non null. On all other destinations,
+     * the ActionBar will show the Up button.
+     * Call [.navigateUp] to handle the Up button.
+     *
+     * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+     *
+     * @param activity The activity hosting the action bar that should be kept in sync with changes
+     * to the NavController.
+     * @param navController The NavController whose navigation actions will be reflected
+     * in the title of the action bar.
+     * @param openableLayout The Openable layout that should be toggled from the home button
+     * @see .setupActionBarWithNavController
+     */
+    @JvmStatic
+    public fun setupActionBarWithNavController(
+        activity: AppCompatActivity,
+        navController: NavController,
+        openableLayout: Openable?
+    ): Unit =
+        setupActionBarWithNavController(
+            activity,
+            navController,
+            AppBarConfiguration.Builder(navController.graph)
+                .setOpenableLayout(openableLayout)
+                .build()
+        )
+
+    /**
+     * Sets up the ActionBar returned by [AppCompatActivity.getSupportActionBar] for use
+     * with a [NavController].
+     *
+     * By calling this method, the title in the action bar will automatically be updated when
+     * the destination changes (assuming there is a valid [label][NavDestination.getLabel]).
+     *
+     * The [AppBarConfiguration] you provide controls how the Navigation button is
+     * displayed.
+     * Call [.navigateUp] to handle the Up button.
+     *
+     * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+     *
+     * @param activity The activity hosting the action bar that should be kept in sync with changes
+     * to the NavController.
+     * @param navController The NavController whose navigation actions will be reflected
+     * in the title of the action bar.
+     * @param configuration Additional configuration options for customizing the behavior of the
+     * ActionBar
+     */
+    @JvmStatic
+    @JvmOverloads
+    public fun setupActionBarWithNavController(
+        activity: AppCompatActivity,
+        navController: NavController,
+        configuration: AppBarConfiguration =
+            AppBarConfiguration.Builder(navController.graph).build()
+    ): Unit =
+        navController.addOnDestinationChangedListener(
+            ActionBarOnDestinationChangedListener(activity, configuration)
+        )
+
+    /**
+     * Sets up a [Toolbar] for use with a [NavController].
+     *
+     * By calling this method, the title in the Toolbar will automatically be updated when
+     * the destination changes (assuming there is a valid [label][NavDestination.getLabel]).
+     *
+     * The start destination of your navigation graph is considered the only top level
+     * destination. On the start destination of your navigation graph, the Toolbar will show
+     * the drawer icon if the given Openable layout is non null. On all other destinations,
+     * the Toolbar will show the Up button. This method will call
+     * [.navigateUp] when the Navigation button is clicked.
+     *
+     * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+     *
+     * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
+     * @param navController The NavController whose navigation actions will be reflected
+     * in the title of the Toolbar.
+     * @param openableLayout The Openable layout that should be toggled from the Navigation button
+     * @see .setupWithNavController
+     */
+    @JvmStatic
+    public fun setupWithNavController(
+        toolbar: Toolbar,
+        navController: NavController,
+        openableLayout: Openable?
+    ): Unit =
+        setupWithNavController(
+            toolbar,
+            navController,
+            AppBarConfiguration.Builder(navController.graph)
+                .setOpenableLayout(openableLayout)
+                .build()
+        )
+
+    /**
+     * Sets up a [Toolbar] for use with a [NavController].
+     *
+     * By calling this method, the title in the Toolbar will automatically be updated when
+     * the destination changes (assuming there is a valid [label][NavDestination.getLabel]).
+     *
+     * The [AppBarConfiguration] you provide controls how the Navigation button is
+     * displayed and what action is triggered when the Navigation button is tapped. This method
+     * will call [.navigateUp] when the Navigation button
+     * is clicked.
+     *
+     * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+     *
+     * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
+     * @param navController The NavController whose navigation actions will be reflected
+     * in the title of the Toolbar.
+     * @param configuration Additional configuration options for customizing the behavior of the
+     * Toolbar
+     */
+    @JvmStatic
+    @JvmOverloads
+    public fun setupWithNavController(
+        toolbar: Toolbar,
+        navController: NavController,
+        configuration: AppBarConfiguration =
+            AppBarConfiguration.Builder(navController.graph).build()
+    ) {
+        navController.addOnDestinationChangedListener(
+            ToolbarOnDestinationChangedListener(toolbar, configuration)
+        )
+        toolbar.setNavigationOnClickListener { navigateUp(navController, configuration) }
+    }
+
+    /**
+     * Sets up a [CollapsingToolbarLayout] and [Toolbar] for use with a
+     * [NavController].
+     *
+     * By calling this method, the title in the CollapsingToolbarLayout will automatically be
+     * updated when the destination changes (assuming there is a valid
+     * [label][NavDestination.getLabel]).
+     *
+     * The start destination of your navigation graph is considered the only top level
+     * destination. On the start destination of your navigation graph, the Toolbar will show
+     * the drawer icon if the given Openable layout is non null. On all other destinations,
+     * the Toolbar will show the Up button. This method will call
+     * [.navigateUp] when the Navigation button is clicked.
+     *
+     * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+     *
+     * @param collapsingToolbarLayout The CollapsingToolbarLayout that should be kept in sync with
+     * changes to the NavController.
+     * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
+     * @param navController The NavController whose navigation actions will be reflected
+     * in the title of the Toolbar.
+     * @param openableLayout The Openable layout that should be toggled from the Navigation button
+     */
+    @JvmStatic
+    public fun setupWithNavController(
+        collapsingToolbarLayout: CollapsingToolbarLayout,
+        toolbar: Toolbar,
+        navController: NavController,
+        openableLayout: Openable?
+    ): Unit =
+        setupWithNavController(
+            collapsingToolbarLayout, toolbar, navController,
+            AppBarConfiguration.Builder(navController.graph)
+                .setOpenableLayout(openableLayout)
+                .build()
+        )
+
+    /**
+     * Sets up a [CollapsingToolbarLayout] and [Toolbar] for use with a
+     * [NavController].
+     *
+     * By calling this method, the title in the CollapsingToolbarLayout will automatically be
+     * updated when the destination changes (assuming there is a valid
+     * [label][NavDestination.getLabel]).
+     *
+     * The [AppBarConfiguration] you provide controls how the Navigation button is
+     * displayed and what action is triggered when the Navigation button is tapped. This method
+     * will call [.navigateUp] when the Navigation button
+     * is clicked.
+     *
+     * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+     *
+     * @param collapsingToolbarLayout The CollapsingToolbarLayout that should be kept in sync with
+     * changes to the NavController.
+     * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
+     * @param navController The NavController whose navigation actions will be reflected
+     * in the title of the Toolbar.
+     * @param configuration Additional configuration options for customizing the behavior of the
+     * Toolbar
+     */
+    @JvmStatic
+    @JvmOverloads
+    public fun setupWithNavController(
+        collapsingToolbarLayout: CollapsingToolbarLayout,
+        toolbar: Toolbar,
+        navController: NavController,
+        configuration: AppBarConfiguration =
+            AppBarConfiguration.Builder(navController.graph).build()
+    ) {
+        navController.addOnDestinationChangedListener(
+            CollapsingToolbarOnDestinationChangedListener(
+                collapsingToolbarLayout, toolbar, configuration
+            )
+        )
+        toolbar.setNavigationOnClickListener { navigateUp(navController, configuration) }
+    }
+
+    /**
+     * Sets up a [NavigationView] for use with a [NavController]. This will call
+     * [.onNavDestinationSelected] when a menu item is selected.
+     * The selected item in the NavigationView will automatically be updated when the destination
+     * changes.
+     *
+     * If the [NavigationView] is directly contained with an [Openable] layout,
+     * it will be closed when a menu item is selected.
+     *
+     * Similarly, if the [NavigationView] has a [BottomSheetBehavior] associated with
+     * it (as is the case when using a [com.google.android.material.bottomsheet.BottomSheetDialog]),
+     * the bottom sheet will be hidden when a menu item is selected.
+     *
+     * @param navigationView The NavigationView that should be kept in sync with changes to the
+     * NavController.
+     * @param navController The NavController that supplies the primary and secondary menu.
+     * Navigation actions on this NavController will be reflected in the
+     * selected item in the NavigationView.
+     */
+    @JvmStatic
+    public fun setupWithNavController(
+        navigationView: NavigationView,
+        navController: NavController
+    ) {
+        navigationView.setNavigationItemSelectedListener { item ->
+            val handled = onNavDestinationSelected(item, navController)
+            if (handled) {
+                val parent = navigationView.parent
+                if (parent is Openable) {
+                    parent.close()
+                } else {
+                    val bottomSheetBehavior = findBottomSheetBehavior(navigationView)
+                    if (bottomSheetBehavior != null) {
+                        bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
+                    }
+                }
+            }
+            handled
+        }
+        val weakReference = WeakReference(navigationView)
+        navController.addOnDestinationChangedListener(
+            object : NavController.OnDestinationChangedListener {
+                override fun onDestinationChanged(
+                    controller: NavController,
+                    destination: NavDestination,
+                    arguments: Bundle?
+                ) {
+                    val view = weakReference.get()
+                    if (view == null) {
+                        navController.removeOnDestinationChangedListener(this)
+                        return
+                    }
+                    view.menu.forEach { item ->
+                        item.isChecked = matchDestination(destination, item.itemId)
+                    }
+                }
+            })
+    }
+
+    /**
+     * Walks up the view hierarchy, trying to determine if the given View is contained within
+     * a bottom sheet.
+     */
+    @JvmStatic
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public fun findBottomSheetBehavior(view: View): BottomSheetBehavior<*>? {
+        val params = view.layoutParams
+        if (params !is CoordinatorLayout.LayoutParams) {
+            val parent = view.parent
+            return if (parent is View) {
+                findBottomSheetBehavior(parent as View)
+            } else null
+        }
+        val behavior = params
+            .behavior
+        return if (behavior !is BottomSheetBehavior<*>) {
+            // We hit a CoordinatorLayout, but the View doesn't have the BottomSheetBehavior
+            null
+        } else behavior
+    }
+
+    /**
+     * Sets up a [BottomNavigationView] for use with a [NavController]. This will call
+     * [.onNavDestinationSelected] when a menu item is selected. The
+     * selected item in the BottomNavigationView will automatically be updated when the destination
+     * changes.
+     *
+     * @param bottomNavigationView The BottomNavigationView that should be kept in sync with
+     * changes to the NavController.
+     * @param navController The NavController that supplies the primary menu.
+     * Navigation actions on this NavController will be reflected in the
+     * selected item in the BottomNavigationView.
+     */
+    @JvmStatic
+    public fun setupWithNavController(
+        bottomNavigationView: BottomNavigationView,
+        navController: NavController
+    ) {
+        bottomNavigationView.setOnNavigationItemSelectedListener { item ->
+            onNavDestinationSelected(
+                item,
+                navController
+            )
+        }
+        val weakReference = WeakReference(bottomNavigationView)
+        navController.addOnDestinationChangedListener(
+            object : NavController.OnDestinationChangedListener {
+                override fun onDestinationChanged(
+                    controller: NavController,
+                    destination: NavDestination,
+                    arguments: Bundle?
+                ) {
+                    val view = weakReference.get()
+                    if (view == null) {
+                        navController.removeOnDestinationChangedListener(this)
+                        return
+                    }
+                    view.menu.forEach { item ->
+                        if (matchDestination(destination, item.itemId)) {
+                            item.isChecked = true
+                        }
+                    }
+                }
+            })
+    }
+
+    /**
+     * Determines whether the given `destId` matches the NavDestination. This handles
+     * both the default case (the destination's id matches the given id) and the nested case where
+     * the given id is a parent/grandparent/etc of the destination.
+     */
+    @JvmStatic
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public fun matchDestination(destination: NavDestination, @IdRes destId: Int): Boolean {
+        var currentDestination: NavDestination? = destination
+        while (currentDestination!!.id != destId && currentDestination.parent != null) {
+            currentDestination = currentDestination.parent
+        }
+        return currentDestination.id == destId
+    }
+
+    /**
+     * Determines whether the given `destinationIds` match the NavDestination. This
+     * handles both the default case (the destination's id is in the given ids) and the nested
+     * case where the given ids is a parent/grandparent/etc of the destination.
+     */
+    @JvmStatic
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public fun matchDestinations(destination: NavDestination, destinationIds: Set<Int?>): Boolean =
+        generateSequence(destination) { it.parent }.all { destinationIds.contains(it.id) }
+
+    /**
+     * Finds the actual start destination of the graph, handling cases where the graph's starting
+     * destination is itself a NavGraph.
+     */
+    @JvmStatic
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public fun findStartDestination(graph: NavGraph): NavDestination =
+        generateSequence(graph.findNode(graph.startDestination)) {
+            if (it is NavGraph) {
+                it.findNode(it.startDestination)
+            } else {
+                null
+            }
+        }.last()
+}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ToolbarOnDestinationChangedListener.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ToolbarOnDestinationChangedListener.kt
index 4e8b808..a5df7f9 100644
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ToolbarOnDestinationChangedListener.kt
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ToolbarOnDestinationChangedListener.kt
@@ -51,7 +51,7 @@
         super.onDestinationChanged(controller, destination, arguments)
     }
 
-    override fun setTitle(title: CharSequence) {
+    override fun setTitle(title: CharSequence?) {
         toolbarWeakReference.get()?.let { toolbar ->
             toolbar.title = title
         }
diff --git a/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt b/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
index c5d7845..0dec76a 100644
--- a/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
@@ -24,6 +24,8 @@
 import androidx.paging.LoadType.PREPEND
 import androidx.paging.LoadType.REFRESH
 import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
 /**
@@ -44,11 +46,17 @@
             dataSource.invalidate()
         }
 
-        // LegacyPagingSource registers invalidate callback after DataSource is created, so we
-        // need to check for race condition here. If DataSource is already invalid, simply
-        // propagate invalidation manually.
-        if (!invalid && dataSource.isInvalid) {
-            invalidate()
+        // dataSource.isInvalid is a @WorkerThread function, so it must be called on
+        // fetchDispatcher. This is normally given since LegacyPagingSource should never be
+        // instantiated on @MainThread, but this workaround exists for Room's current
+        // implementation which is a common use-case. See b/178636235.
+        GlobalScope.launch(fetchDispatcher) {
+            // LegacyPagingSource registers invalidate callback after DataSource is created, so we
+            // need to check for race condition here. If DataSource is already invalid, simply
+            // propagate invalidation manually.
+            if (!invalid && dataSource.isInvalid) {
+                invalidate()
+            }
         }
     }
 
diff --git a/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
index add2986..ce0e159 100644
--- a/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
@@ -17,6 +17,7 @@
 package androidx.paging
 
 import androidx.paging.PagingSource.LoadResult.Page
+import androidx.testutils.TestDispatcher
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asCoroutineDispatcher
@@ -53,6 +54,37 @@
     )
 
     @Test
+    fun init_invalidateOnFetchDispatcher() {
+        val testDispatcher = TestDispatcher()
+        val dataSource = object : DataSource<Int, Int>(KeyType.ITEM_KEYED) {
+            var isInvalidCalls = 0
+
+            override val isInvalid: Boolean
+                get() {
+                    isInvalidCalls++
+                    return super.isInvalid
+                }
+
+            override suspend fun load(params: Params<Int>): BaseResult<Int> {
+                return BaseResult(listOf(), null, null)
+            }
+
+            override fun getKeyInternal(item: Int): Int = 0
+        }
+
+        // init will immediately trigger a call to DataSource.isInvalid, but if it's launched on
+        // fetchDispatcher, it should block on testDispatcher.executeAll().
+        LegacyPagingSource(
+            fetchDispatcher = testDispatcher,
+            dataSource = dataSource,
+        )
+
+        assertEquals(0, dataSource.isInvalidCalls)
+        testDispatcher.executeAll()
+        assertEquals(1, dataSource.isInvalidCalls)
+    }
+
+    @Test
     fun item() {
         @Suppress("DEPRECATION")
         val dataSource = object : ItemKeyedDataSource<Int, String>() {
@@ -330,17 +362,22 @@
             }
         }
 
-        val pagingSourceFactory = dataSourceFactory.asPagingSourceFactory().let {
+        val testDispatcher = TestDispatcher()
+        val pagingSourceFactory = dataSourceFactory.asPagingSourceFactory(
+            fetchDispatcher = testDispatcher
+        ).let {
             { it() as LegacyPagingSource }
         }
 
         val pagingSource0 = pagingSourceFactory()
+        testDispatcher.executeAll()
         assertTrue { pagingSource0.dataSource.isInvalid }
         assertTrue { pagingSource0.invalid }
         assertTrue { dataSourceFactory.dataSources[0].isInvalid }
         assertEquals(dataSourceFactory.dataSources[0], pagingSource0.dataSource)
 
         val pagingSource1 = pagingSourceFactory()
+        testDispatcher.executeAll()
         assertFalse { pagingSource1.dataSource.isInvalid }
         assertFalse { pagingSource1.invalid }
         assertFalse { dataSourceFactory.dataSources[1].isInvalid }
diff --git a/settings.gradle b/settings.gradle
index 41e1eb6..9e47bd3 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -138,6 +138,7 @@
 includeProject(":appcompat:appcompat-benchmark", "appcompat/appcompat-benchmark", [BuildType.MAIN])
 includeProject(":appcompat:appcompat-lint", "appcompat/appcompat-lint", [BuildType.MAIN])
 includeProject(":appcompat:appcompat-resources", "appcompat/appcompat-resources", [BuildType.MAIN])
+includeProject(":appcompat:integration-tests:receive-content-testapp", "appcompat/integration-tests/receive-content-testapp", [BuildType.MAIN])
 includeProject(":appsearch:appsearch", "appsearch/appsearch", [BuildType.MAIN])
 includeProject(":appsearch:appsearch-compiler", "appsearch/compiler", [BuildType.MAIN])
 includeProject(":appsearch:appsearch-local-storage", "appsearch/local-storage", [BuildType.MAIN])
diff --git a/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java b/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java
index 764f5cb..3519721 100644
--- a/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java
+++ b/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java
@@ -59,7 +59,7 @@
 import androidx.wear.watchface.style.UserStyleRepository;
 import androidx.wear.watchface.style.UserStyleSchema;
 
-import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -72,6 +72,8 @@
 import java.util.ArrayList;
 import java.util.concurrent.TimeUnit;
 
+import kotlin.coroutines.Continuation;
+
 /** Tests for {@link ComplicationDrawable}. */
 @RunWith(ComplicationsTestRunner.class)
 @DoNotInstrument
@@ -713,11 +715,11 @@
             ApplicationProvider.getApplicationContext().startActivity(intent);
         }
 
-        @NonNull
+        @Nullable
         @Override
-        protected WatchFace createWatchFace(
-                @NotNull SurfaceHolder surfaceHolder,
-                @NotNull WatchState watchState) {
+        protected Object createWatchFace(@NonNull SurfaceHolder surfaceHolder,
+                @NonNull WatchState watchState,
+                @NonNull Continuation<? super WatchFace> completion) {
             UserStyleRepository userStyleRepository =
                     new UserStyleRepository(new UserStyleSchema(new ArrayList<>()));
             return new WatchFace(
diff --git a/wear/wear-watchface-editor/api/current.txt b/wear/wear-watchface-editor/api/current.txt
index 32da307..6ad0951 100644
--- a/wear/wear-watchface-editor/api/current.txt
+++ b/wear/wear-watchface-editor/api/current.txt
@@ -2,16 +2,16 @@
 package androidx.wear.watchface.editor {
 
   public final class EditorRequest {
-    ctor public EditorRequest(android.content.ComponentName watchFaceComponentName, android.content.ComponentName editorComponentName, String watchFaceInstanceId, java.util.Map<java.lang.String,java.lang.String>? initialUserStyle);
+    ctor public EditorRequest(android.content.ComponentName watchFaceComponentName, android.content.ComponentName editorComponentName, String? watchFaceInstanceId, java.util.Map<java.lang.String,java.lang.String>? initialUserStyle);
     method public static androidx.wear.watchface.editor.EditorRequest? createFromIntent(android.content.Intent intent);
     method public android.content.ComponentName getEditorComponentName();
     method public java.util.Map<java.lang.String,java.lang.String>? getInitialUserStyle();
     method public android.content.ComponentName getWatchFaceComponentName();
-    method public String getWatchFaceInstanceId();
+    method public String? getWatchFaceInstanceId();
     property public final android.content.ComponentName editorComponentName;
     property public final java.util.Map<java.lang.String,java.lang.String>? initialUserStyle;
     property public final android.content.ComponentName watchFaceComponentName;
-    property public final String watchFaceInstanceId;
+    property public final String? watchFaceInstanceId;
     field public static final androidx.wear.watchface.editor.EditorRequest.Companion Companion;
   }
 
@@ -20,11 +20,13 @@
   }
 
   public final class EditorResult {
-    ctor public EditorResult(java.util.Map<java.lang.String,java.lang.String> userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData> previewComplicationData);
+    ctor public EditorResult(java.util.Map<java.lang.String,java.lang.String> userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData> previewComplicationData, String? watchFaceInstanceId);
     method public java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData> getPreviewComplicationData();
     method public java.util.Map<java.lang.String,java.lang.String> getUserStyle();
+    method public String? getWatchFaceInstanceId();
     property public final java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData> previewComplicationData;
     property public final java.util.Map<java.lang.String,java.lang.String> userStyle;
+    property public final String? watchFaceInstanceId;
   }
 
   public interface EditorSession {
@@ -34,7 +36,7 @@
     method @UiThread public Integer? getComplicationIdAt(@Px int x, @Px int y);
     method public com.google.common.util.concurrent.ListenableFuture<java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData>> getComplicationPreviewData();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> getComplicationState();
-    method public String getInstanceId();
+    method public String? getInstanceId();
     method public long getPreviewReferenceTimeMillis();
     method public androidx.wear.watchface.style.UserStyle getUserStyle();
     method public androidx.wear.watchface.style.UserStyleSchema getUserStyleSchema();
@@ -45,7 +47,7 @@
     property public abstract Integer? backgroundComplicationId;
     property public abstract com.google.common.util.concurrent.ListenableFuture<java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData>> complicationPreviewData;
     property public abstract java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> complicationState;
-    property public abstract String instanceId;
+    property public abstract String? instanceId;
     property public abstract long previewReferenceTimeMillis;
     property public abstract androidx.wear.watchface.style.UserStyle userStyle;
     property public abstract androidx.wear.watchface.style.UserStyleSchema userStyleSchema;
@@ -73,5 +75,8 @@
   public static final class WatchFaceEditorContract.Companion {
   }
 
+  public final class WatchFaceEditorContractKt {
+  }
+
 }
 
diff --git a/wear/wear-watchface-editor/api/public_plus_experimental_current.txt b/wear/wear-watchface-editor/api/public_plus_experimental_current.txt
index 32da307..6ad0951 100644
--- a/wear/wear-watchface-editor/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface-editor/api/public_plus_experimental_current.txt
@@ -2,16 +2,16 @@
 package androidx.wear.watchface.editor {
 
   public final class EditorRequest {
-    ctor public EditorRequest(android.content.ComponentName watchFaceComponentName, android.content.ComponentName editorComponentName, String watchFaceInstanceId, java.util.Map<java.lang.String,java.lang.String>? initialUserStyle);
+    ctor public EditorRequest(android.content.ComponentName watchFaceComponentName, android.content.ComponentName editorComponentName, String? watchFaceInstanceId, java.util.Map<java.lang.String,java.lang.String>? initialUserStyle);
     method public static androidx.wear.watchface.editor.EditorRequest? createFromIntent(android.content.Intent intent);
     method public android.content.ComponentName getEditorComponentName();
     method public java.util.Map<java.lang.String,java.lang.String>? getInitialUserStyle();
     method public android.content.ComponentName getWatchFaceComponentName();
-    method public String getWatchFaceInstanceId();
+    method public String? getWatchFaceInstanceId();
     property public final android.content.ComponentName editorComponentName;
     property public final java.util.Map<java.lang.String,java.lang.String>? initialUserStyle;
     property public final android.content.ComponentName watchFaceComponentName;
-    property public final String watchFaceInstanceId;
+    property public final String? watchFaceInstanceId;
     field public static final androidx.wear.watchface.editor.EditorRequest.Companion Companion;
   }
 
@@ -20,11 +20,13 @@
   }
 
   public final class EditorResult {
-    ctor public EditorResult(java.util.Map<java.lang.String,java.lang.String> userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData> previewComplicationData);
+    ctor public EditorResult(java.util.Map<java.lang.String,java.lang.String> userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData> previewComplicationData, String? watchFaceInstanceId);
     method public java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData> getPreviewComplicationData();
     method public java.util.Map<java.lang.String,java.lang.String> getUserStyle();
+    method public String? getWatchFaceInstanceId();
     property public final java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData> previewComplicationData;
     property public final java.util.Map<java.lang.String,java.lang.String> userStyle;
+    property public final String? watchFaceInstanceId;
   }
 
   public interface EditorSession {
@@ -34,7 +36,7 @@
     method @UiThread public Integer? getComplicationIdAt(@Px int x, @Px int y);
     method public com.google.common.util.concurrent.ListenableFuture<java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData>> getComplicationPreviewData();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> getComplicationState();
-    method public String getInstanceId();
+    method public String? getInstanceId();
     method public long getPreviewReferenceTimeMillis();
     method public androidx.wear.watchface.style.UserStyle getUserStyle();
     method public androidx.wear.watchface.style.UserStyleSchema getUserStyleSchema();
@@ -45,7 +47,7 @@
     property public abstract Integer? backgroundComplicationId;
     property public abstract com.google.common.util.concurrent.ListenableFuture<java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData>> complicationPreviewData;
     property public abstract java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> complicationState;
-    property public abstract String instanceId;
+    property public abstract String? instanceId;
     property public abstract long previewReferenceTimeMillis;
     property public abstract androidx.wear.watchface.style.UserStyle userStyle;
     property public abstract androidx.wear.watchface.style.UserStyleSchema userStyleSchema;
@@ -73,5 +75,8 @@
   public static final class WatchFaceEditorContract.Companion {
   }
 
+  public final class WatchFaceEditorContractKt {
+  }
+
 }
 
diff --git a/wear/wear-watchface-editor/api/restricted_current.txt b/wear/wear-watchface-editor/api/restricted_current.txt
index 32da307..6ad0951 100644
--- a/wear/wear-watchface-editor/api/restricted_current.txt
+++ b/wear/wear-watchface-editor/api/restricted_current.txt
@@ -2,16 +2,16 @@
 package androidx.wear.watchface.editor {
 
   public final class EditorRequest {
-    ctor public EditorRequest(android.content.ComponentName watchFaceComponentName, android.content.ComponentName editorComponentName, String watchFaceInstanceId, java.util.Map<java.lang.String,java.lang.String>? initialUserStyle);
+    ctor public EditorRequest(android.content.ComponentName watchFaceComponentName, android.content.ComponentName editorComponentName, String? watchFaceInstanceId, java.util.Map<java.lang.String,java.lang.String>? initialUserStyle);
     method public static androidx.wear.watchface.editor.EditorRequest? createFromIntent(android.content.Intent intent);
     method public android.content.ComponentName getEditorComponentName();
     method public java.util.Map<java.lang.String,java.lang.String>? getInitialUserStyle();
     method public android.content.ComponentName getWatchFaceComponentName();
-    method public String getWatchFaceInstanceId();
+    method public String? getWatchFaceInstanceId();
     property public final android.content.ComponentName editorComponentName;
     property public final java.util.Map<java.lang.String,java.lang.String>? initialUserStyle;
     property public final android.content.ComponentName watchFaceComponentName;
-    property public final String watchFaceInstanceId;
+    property public final String? watchFaceInstanceId;
     field public static final androidx.wear.watchface.editor.EditorRequest.Companion Companion;
   }
 
@@ -20,11 +20,13 @@
   }
 
   public final class EditorResult {
-    ctor public EditorResult(java.util.Map<java.lang.String,java.lang.String> userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData> previewComplicationData);
+    ctor public EditorResult(java.util.Map<java.lang.String,java.lang.String> userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData> previewComplicationData, String? watchFaceInstanceId);
     method public java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData> getPreviewComplicationData();
     method public java.util.Map<java.lang.String,java.lang.String> getUserStyle();
+    method public String? getWatchFaceInstanceId();
     property public final java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData> previewComplicationData;
     property public final java.util.Map<java.lang.String,java.lang.String> userStyle;
+    property public final String? watchFaceInstanceId;
   }
 
   public interface EditorSession {
@@ -34,7 +36,7 @@
     method @UiThread public Integer? getComplicationIdAt(@Px int x, @Px int y);
     method public com.google.common.util.concurrent.ListenableFuture<java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData>> getComplicationPreviewData();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> getComplicationState();
-    method public String getInstanceId();
+    method public String? getInstanceId();
     method public long getPreviewReferenceTimeMillis();
     method public androidx.wear.watchface.style.UserStyle getUserStyle();
     method public androidx.wear.watchface.style.UserStyleSchema getUserStyleSchema();
@@ -45,7 +47,7 @@
     property public abstract Integer? backgroundComplicationId;
     property public abstract com.google.common.util.concurrent.ListenableFuture<java.util.Map<java.lang.Integer,androidx.wear.complications.data.ComplicationData>> complicationPreviewData;
     property public abstract java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationState> complicationState;
-    property public abstract String instanceId;
+    property public abstract String? instanceId;
     property public abstract long previewReferenceTimeMillis;
     property public abstract androidx.wear.watchface.style.UserStyle userStyle;
     property public abstract androidx.wear.watchface.style.UserStyleSchema userStyleSchema;
@@ -73,5 +75,8 @@
   public static final class WatchFaceEditorContract.Companion {
   }
 
+  public final class WatchFaceEditorContractKt {
+  }
+
 }
 
diff --git a/wear/wear-watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/WatchFaceConfigActivity.kt b/wear/wear-watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/WatchFaceConfigActivity.kt
index f5cb0b5..7246485 100644
--- a/wear/wear-watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/WatchFaceConfigActivity.kt
+++ b/wear/wear-watchface-editor/samples/src/main/java/androidx/wear/watchface/editor/sample/WatchFaceConfigActivity.kt
@@ -80,10 +80,7 @@
         super.onCreate(savedInstanceState)
         val editorSession = EditorSession.createOnWatchEditingSession(
             this,
-            intent!!.apply {
-                // TODO(alexclarke): Remove this when SysUI creates compatible intents.
-                putExtra("INSTANCE_ID", "FakeID")
-            }
+            intent!!
         )!!
         init(
             editorSession,
diff --git a/wear/wear-watchface-editor/src/androidTest/java/androidx.wear.watchface.editor.EditingSessionTest.kt b/wear/wear-watchface-editor/src/androidTest/java/androidx.wear.watchface.editor.EditingSessionTest.kt
index b34117b..eb841f6 100644
--- a/wear/wear-watchface-editor/src/androidTest/java/androidx.wear.watchface.editor.EditingSessionTest.kt
+++ b/wear/wear-watchface-editor/src/androidTest/java/androidx.wear.watchface.editor.EditingSessionTest.kt
@@ -150,6 +150,11 @@
     }
 }
 
+// Disables the requirement that watchFaceInstanceId has to be non-null on R and above.
+private class WatchFaceEditorContractForTest : WatchFaceEditorContract() {
+    override fun nullWatchFaceInstanceIdOK() = true
+}
+
 @RunWith(AndroidJUnit4::class)
 @MediumTest
 public class EditorSessionTest {
@@ -246,6 +251,7 @@
     private fun createOnWatchFaceEditingTestActivity(
         userStyleSettings: List<UserStyleSetting>,
         complications: List<Complication>,
+        instanceId: String? = testInstanceId,
         previewReferenceTimeMillis: Long = 12345
     ): ActivityScenario<OnWatchFaceEditingTestActivity> {
         val userStyleRepository = UserStyleRepository(UserStyleSchema(userStyleSettings))
@@ -258,9 +264,9 @@
         `when`(editorDelegate.previewReferenceTimeMillis).thenReturn(previewReferenceTimeMillis)
 
         return ActivityScenario.launch(
-            WatchFaceEditorContract().createIntent(
+            WatchFaceEditorContractForTest().createIntent(
                 ApplicationProvider.getApplicationContext<Context>(),
-                EditorRequest(testComponentName, testEditorComponentName, testInstanceId, null)
+                EditorRequest(testComponentName, testEditorComponentName, instanceId, null)
             ).apply {
                 component = ComponentName(
                     ApplicationProvider.getApplicationContext<Context>(),
@@ -296,7 +302,11 @@
 
     @Test
     public fun previewReferenceTimeMillis() {
-        val scenario = createOnWatchFaceEditingTestActivity(emptyList(), emptyList(), 54321L)
+        val scenario = createOnWatchFaceEditingTestActivity(
+            emptyList(),
+            emptyList(),
+            previewReferenceTimeMillis = 54321L
+        )
         scenario.onActivity {
             assertThat(it.editorSession.previewReferenceTimeMillis).isEqualTo(54321L)
         }
@@ -583,6 +593,7 @@
 
         assertThat(result.userStyle[colorStyleSetting.id]).isEqualTo(blueStyleOption.id)
         assertThat(result.userStyle[watchHandStyleSetting.id]).isEqualTo(gothicStyleOption.id)
+        assertThat(result.watchFaceInstanceId).isEqualTo(testInstanceId)
 
         assertThat(result.previewComplicationData.size).isEqualTo(2)
         val leftComplicationData = result.previewComplicationData[LEFT_COMPLICATION_ID] as
@@ -605,6 +616,26 @@
     }
 
     @Test
+    public fun nullInstanceId() {
+        val scenario = createOnWatchFaceEditingTestActivity(
+            listOf(colorStyleSetting, watchHandStyleSetting),
+            emptyList(),
+            instanceId = null
+        )
+        scenario.onActivity { activity ->
+            assertThat(activity.editorSession.instanceId).isNull()
+            activity.setWatchRequestResult(activity.editorSession)
+            activity.finish()
+        }
+        assertThat(
+            WatchFaceEditorContractForTest().parseResult(
+                scenario.result.resultCode,
+                scenario.result.resultData
+            ).watchFaceInstanceId
+        ).isNull()
+    }
+
+    @Test
     public fun emptyComplicationPreviewDataInActivityResult() {
         val scenario = createOnWatchFaceEditingTestActivity(emptyList(), emptyList())
         scenario.onActivity { activity ->
@@ -621,7 +652,7 @@
     }
 
     @Test
-    fun watchFaceEditorContract_createIntent() {
+    public fun watchFaceEditorContract_createIntent() {
         val intent = WatchFaceEditorContract().createIntent(
             ApplicationProvider.getApplicationContext<Context>(),
             EditorRequest(testComponentName, testEditorComponentName, testInstanceId, null)
diff --git a/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt b/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
index 9bcc69b..be1b642 100644
--- a/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
+++ b/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
@@ -62,10 +62,11 @@
     public val watchFaceComponentName: ComponentName
 
     /**
-     * The instance id of the watch face being edited. Note each distinct [ComponentName] can have
+     * Unique ID for the instance of the watch face being edited, only defined for Android R and
+     * beyond, it's `null` on Android P and earlier. Note each distinct [ComponentName] can have
      * multiple instances.
      */
-    public val instanceId: String
+    public val instanceId: String?
 
     /** The current [UserStyle]. Assigning to this will cause the style to update. */
     public var userStyle: UserStyle
@@ -398,7 +399,7 @@
 internal class OnWatchFaceEditorSessionImpl(
     activity: ComponentActivity,
     override val watchFaceComponentName: ComponentName,
-    override val instanceId: String,
+    override val instanceId: String?,
     initialEditorUserStyle: Map<String, String>?,
     private val editorDelegate: WatchFace.EditorDelegate,
     providerInfoRetrieverProvider: ProviderInfoRetrieverProvider
@@ -456,7 +457,7 @@
     activity: ComponentActivity,
     private val headlessWatchFaceClient: HeadlessWatchFaceClient,
     override val watchFaceComponentName: ComponentName,
-    override val instanceId: String,
+    override val instanceId: String?,
     initialUserStyle: Map<String, String>,
     providerInfoRetrieverProvider: ProviderInfoRetrieverProvider
 ) : BaseEditorSession(activity, providerInfoRetrieverProvider) {
@@ -522,12 +523,12 @@
         Activity.RESULT_OK,
         Intent().apply {
             putExtra(
-                EditorResult.USER_STYLE_KEY,
+                USER_STYLE_KEY,
                 ParcelUtils.toParcelable(editorSession.userStyle.toWireFormat())
             )
             val data = editorSession.complicationPreviewData.get()
             putExtra(
-                EditorResult.PREVIEW_COMPLICATIONS_KEY,
+                PREVIEW_COMPLICATIONS_KEY,
                 data.map {
                     ParcelUtils.toParcelable(
                         IdAndComplicationDataWireFormat(
@@ -537,6 +538,7 @@
                     )
                 }.toTypedArray()
             )
+            putExtra(INSTANCE_ID_KEY, editorSession.instanceId)
         }
     )
 }
diff --git a/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/WatchFaceEditorContract.kt b/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/WatchFaceEditorContract.kt
index 2899604..184deae 100644
--- a/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/WatchFaceEditorContract.kt
+++ b/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/WatchFaceEditorContract.kt
@@ -20,6 +20,7 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.os.Build
 import android.support.wearable.watchface.Constants
 import androidx.activity.result.contract.ActivityResultContract
 import androidx.versionedparcelable.ParcelUtils
@@ -29,6 +30,12 @@
 import androidx.wear.watchface.style.UserStyle
 import androidx.wear.watchface.style.data.UserStyleWireFormat
 
+internal const val INSTANCE_ID_KEY: String = "INSTANCE_ID_KEY"
+internal const val COMPONENT_NAME_KEY: String = "COMPONENT_NAME_KEY"
+internal const val PREVIEW_COMPLICATIONS_KEY: String = "PREVIEW_COMPLICATIONS_KEY"
+internal const val USER_STYLE_KEY: String = "USER_STYLE_KEY"
+internal const val USER_STYLE_VALUES: String = "USER_STYLE_VALUES"
+
 /**
  * The request sent by [WatchFaceEditorContract.createIntent]. The editing session's result should
  * be reported via [Activity.setWatchRequestResult].
@@ -40,8 +47,12 @@
     /** The [ComponentName] of the watch face editor. */
     public val editorComponentName: ComponentName,
 
-    /** A unique ID for the instance of the watch face being edited. */
-    public val watchFaceInstanceId: String,
+    /**
+     * Unique ID for the instance of the watch face being edited, only defined for Android R and
+     * beyond, it's `null` on Android P and earlier. Note each distinct [ComponentName] can have
+     * multiple instances.
+     */
+    public val watchFaceInstanceId: String?,
 
     /** The initial [UserStyle], only required for a headless [EditorSession]. */
     public val initialUserStyle: Map<String, String>?
@@ -57,8 +68,8 @@
                 intent.getParcelableExtra<ComponentName>(COMPONENT_NAME_KEY)
                     ?: intent.getParcelableExtra(Constants.EXTRA_WATCH_FACE_COMPONENT)
             val editorComponentName = intent.component ?: ComponentName("?", "?")
-            val instanceId = intent.getStringExtra(INSTANCE_ID)!!
-            val userStyleKey = intent.getStringArrayExtra(USER_STYLE_KEYS)
+            val instanceId = intent.getStringExtra(INSTANCE_ID_KEY)
+            val userStyleKey = intent.getStringArrayExtra(USER_STYLE_KEY)
             val userStyleValue = intent.getStringArrayExtra(USER_STYLE_VALUES)
             return componentName?.let {
                 if (userStyleKey != null && userStyleValue != null &&
@@ -79,11 +90,6 @@
                 }
             }
         }
-
-        internal const val COMPONENT_NAME_KEY: String = "COMPONENT_NAME_KEY"
-        internal const val INSTANCE_ID: String = "INSTANCE_ID"
-        internal const val USER_STYLE_KEYS: String = "USER_STYLE_KEYS"
-        internal const val USER_STYLE_VALUES: String = "USER_STYLE_VALUES"
     }
 }
 
@@ -98,13 +104,15 @@
      * The preview [ComplicationData] used by the editor, which can be used by a headless client to
      * take an updated screen shot.
      */
-    public val previewComplicationData: Map<Int, ComplicationData>
-) {
-    internal companion object {
-        internal const val USER_STYLE_KEY: String = "USER_STYLE_KEY"
-        internal const val PREVIEW_COMPLICATIONS_KEY: String = "PREVIEW_COMPLICATIONS_KEY"
-    }
-}
+    public val previewComplicationData: Map<Int, ComplicationData>,
+
+    /**
+     * Unique ID for the instance of the watch face being edited, only defined for Android R and
+     * beyond, it's `null` on Android P and earlier. Note each distinct [ComponentName] can have
+     * multiple instances.
+     */
+    public val watchFaceInstanceId: String?
+)
 
 /** An [ActivityResultContract] for invoking a watch face editor. */
 public open class WatchFaceEditorContract : ActivityResultContract<EditorRequest, EditorResult>() {
@@ -114,19 +122,29 @@
             "androidx.wear.watchface.editor.action.WATCH_FACE_EDITOR"
     }
 
+    // Required for testing.
+    internal open fun nullWatchFaceInstanceIdOK() =
+        Build.VERSION.SDK_INT < Build.VERSION_CODES.R
+
     override fun createIntent(
         context: Context,
         input: EditorRequest
-    ): Intent =
-        Intent(ACTION_WATCH_FACE_EDITOR).apply {
+    ): Intent {
+        require(
+            input.watchFaceInstanceId != null || nullWatchFaceInstanceIdOK()
+        ) {
+            "watchFaceInstanceId must be set from Android R and above"
+        }
+        return Intent(ACTION_WATCH_FACE_EDITOR).apply {
             component = input.editorComponentName
-            putExtra(EditorRequest.COMPONENT_NAME_KEY, input.watchFaceComponentName)
-            putExtra(EditorRequest.INSTANCE_ID, input.watchFaceInstanceId)
+            putExtra(COMPONENT_NAME_KEY, input.watchFaceComponentName)
+            putExtra(INSTANCE_ID_KEY, input.watchFaceInstanceId)
             input.initialUserStyle?.let {
-                putExtra(EditorRequest.USER_STYLE_KEYS, it.keys.toTypedArray())
-                putExtra(EditorRequest.USER_STYLE_VALUES, it.values.toTypedArray())
+                putExtra(USER_STYLE_KEY, it.keys.toTypedArray())
+                putExtra(USER_STYLE_VALUES, it.values.toTypedArray())
             }
         }
+    }
 
     override fun parseResult(resultCode: Int, intent: Intent?): EditorResult {
         val extras = intent!!.extras!!
@@ -134,18 +152,20 @@
         return EditorResult(
             // Unmarshall the UserStyle.
             ParcelUtils.fromParcelable<UserStyleWireFormat>(
-                extras.getParcelable(EditorResult.USER_STYLE_KEY)!!
+                extras.getParcelable(USER_STYLE_KEY)!!
             ).mUserStyle,
 
             // Unmarshall the preview Map<Int, ComplicationData>.
-            extras.getParcelableArray(EditorResult.PREVIEW_COMPLICATIONS_KEY)?.let {
+            extras.getParcelableArray(PREVIEW_COMPLICATIONS_KEY)?.let {
                 it.map {
                     ParcelUtils.fromParcelable<IdAndComplicationDataWireFormat>(it)
                 }.associateBy(
                     { it.id },
                     { it.complicationData.asApiComplicationData() }
                 )
-            } ?: emptyMap()
+            } ?: emptyMap(),
+
+            extras.getString(INSTANCE_ID_KEY)
         )
     }
 }
diff --git a/wear/wear-watchface/api/current.txt b/wear/wear-watchface/api/current.txt
index b9bf6f7..6179d24 100644
--- a/wear/wear-watchface/api/current.txt
+++ b/wear/wear-watchface/api/current.txt
@@ -244,7 +244,7 @@
 
   public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
     ctor public WatchFaceService();
-    method protected abstract androidx.wear.watchface.WatchFace createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState);
+    method protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace> p);
     method public final android.service.wallpaper.WallpaperService.Engine onCreateEngine();
   }
 
diff --git a/wear/wear-watchface/api/public_plus_experimental_current.txt b/wear/wear-watchface/api/public_plus_experimental_current.txt
index b9bf6f7..6179d24 100644
--- a/wear/wear-watchface/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface/api/public_plus_experimental_current.txt
@@ -244,7 +244,7 @@
 
   public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
     ctor public WatchFaceService();
-    method protected abstract androidx.wear.watchface.WatchFace createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState);
+    method protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace> p);
     method public final android.service.wallpaper.WallpaperService.Engine onCreateEngine();
   }
 
diff --git a/wear/wear-watchface/api/restricted_current.txt b/wear/wear-watchface/api/restricted_current.txt
index f43ccbf..2c22f3b 100644
--- a/wear/wear-watchface/api/restricted_current.txt
+++ b/wear/wear-watchface/api/restricted_current.txt
@@ -305,7 +305,7 @@
 
   public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
     ctor public WatchFaceService();
-    method protected abstract androidx.wear.watchface.WatchFace createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState);
+    method protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace> p);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public android.view.SurfaceHolder? getWallpaperSurfaceHolderOverride();
     method public final android.service.wallpaper.WallpaperService.Engine onCreateEngine();
   }
diff --git a/wear/wear-watchface/build.gradle b/wear/wear-watchface/build.gradle
index e9b2d20..0fb9eb6 100644
--- a/wear/wear-watchface/build.gradle
+++ b/wear/wear-watchface/build.gradle
@@ -34,8 +34,8 @@
     api(project(":wear:wear-watchface-data"))
     api(project(":wear:wear-watchface-style"))
     api(KOTLIN_STDLIB)
+    api(KOTLIN_COROUTINES_ANDROID)
 
-    implementation("androidx.concurrent:concurrent-futures:1.0.0")
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.recyclerview:recyclerview:1.1.0")
 
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
index d420461..cb651ed 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
@@ -89,7 +89,7 @@
 
 /** A simple example canvas based analog watch face. */
 open class ExampleCanvasAnalogWatchFaceService : WatchFaceService() {
-    override fun createWatchFace(
+    override suspend fun createWatchFace(
         surfaceHolder: SurfaceHolder,
         watchState: WatchState
     ) = createExampleCanvasAnalogWatchFaceBuilder(
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt
index 372c513..bfbeb06 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt
@@ -464,7 +464,7 @@
 
 /** A simple example canvas based digital watch face. */
 class ExampleCanvasDigitalWatchFaceService : WatchFaceService() {
-    override fun createWatchFace(
+    override suspend fun createWatchFace(
         surfaceHolder: SurfaceHolder,
         watchState: WatchState
     ): WatchFace {
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
index 21d66c8..42f713f 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
@@ -74,7 +74,7 @@
  * when the watch enters ambient mode.
  */
 class ExampleOpenGLWatchFaceService() : WatchFaceService() {
-    override fun createWatchFace(
+    override suspend fun createWatchFace(
         surfaceHolder: SurfaceHolder,
         watchState: WatchState
     ) = createExampleOpenGLWatchFaceBuilder(
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
index 7b4bd98d..8df800e 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
@@ -48,7 +48,7 @@
 fun kDocCreateExampleWatchFaceService(): WatchFaceService {
 
     class ExampleCanvasWatchFaceService : WatchFaceService() {
-        override fun createWatchFace(
+        override suspend fun createWatchFace(
             surfaceHolder: SurfaceHolder,
             watchState: WatchState
         ): WatchFace {
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasAnalogWatchFaceService.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasAnalogWatchFaceService.kt
index 6a12429..88e7c04 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasAnalogWatchFaceService.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasAnalogWatchFaceService.kt
@@ -40,7 +40,7 @@
         attachBaseContext(testContext)
     }
 
-    override fun createWatchFace(
+    override suspend fun createWatchFace(
         surfaceHolder: SurfaceHolder,
         watchState: WatchState
     ): WatchFace {
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGlesWatchFaceService.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGlesWatchFaceService.kt
index 32c1e93..9cc1d57 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGlesWatchFaceService.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestGlesWatchFaceService.kt
@@ -39,7 +39,7 @@
         attachBaseContext(testContext)
     }
 
-    override fun createWatchFace(
+    override suspend fun createWatchFace(
         surfaceHolder: SurfaceHolder,
         watchState: WatchState
     ): WatchFace {
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
index bbb5267..85d8f9b 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
@@ -211,6 +211,14 @@
         interactiveWatchFaceInstanceSysUi.release()
     }
 
+    private fun waitForPendingTaskToRunOnHandler() {
+        val latch = CountDownLatch(1)
+        handler.post {
+            latch.countDown()
+        }
+        latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+    }
+
     @Test
     fun testActiveScreenshot() {
         handler.post(this::initCanvasWatchFace)
@@ -415,6 +423,9 @@
         )
 
         handler.post(this::initCanvasWatchFace)
+        // Preview complication data results in additional tasks posted which we need to complete
+        // before interactiveWatchFaceInstanceWCS is initialized.
+        waitForPendingTaskToRunOnHandler()
         var bitmap: Bitmap? = null
         handler.post {
             bitmap = SharedMemoryImage.ashmemCompressedImageBundleToBitmap(
@@ -468,7 +479,7 @@
             )
 
             val engineWrapper2 = service2.onCreateEngine() as WatchFaceService.EngineWrapper
-            engineWrapper2.draw()
+            handler.post { engineWrapper2.draw() }
         }
 
         renderDoneLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index c2aac9f..aa88d92 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -546,14 +546,16 @@
     private val batteryLowAndNotChargingObserver = Observer<Boolean> {
         // To save power we request a lower hardware display frame rate when the battery is low
         // and not charging.
-        renderer.surfaceHolder.surface.setFrameRate(
-            if (it) {
-                1000f / MAX_LOW_POWER_INTERACTIVE_UPDATE_RATE_MS.toFloat()
-            } else {
-                SYSTEM_DECIDES_FRAME_RATE
-            },
-            FRAME_RATE_COMPATIBILITY_DEFAULT
-        )
+        if (renderer.surfaceHolder.surface.isValid) {
+            renderer.surfaceHolder.surface.setFrameRate(
+                if (it) {
+                    1000f / MAX_LOW_POWER_INTERACTIVE_UPDATE_RATE_MS.toFloat()
+                } else {
+                    SYSTEM_DECIDES_FRAME_RATE
+                },
+                FRAME_RATE_COMPATIBILITY_DEFAULT
+            )
+        }
     }
 
     init {
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index d497ee6..f6bf46c 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -69,6 +69,9 @@
 import androidx.wear.watchface.style.UserStyleRepository
 import androidx.wear.watchface.style.UserStyleSetting
 import androidx.wear.watchface.style.data.UserStyleWireFormat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.android.asCoroutineDispatcher
+import kotlinx.coroutines.launch
 import java.io.FileNotFoundException
 import java.util.concurrent.CountDownLatch
 
@@ -218,7 +221,7 @@
     }
 
     /** Override this factory method to create your WatchFaceImpl. */
-    protected abstract fun createWatchFace(
+    protected abstract suspend fun createWatchFace(
         surfaceHolder: SurfaceHolder,
         watchState: WatchState
     ): WatchFace
@@ -252,6 +255,7 @@
     internal inner class EngineWrapper(
         private val uiThreadHandler: Handler
     ) : WallpaperService.Engine(), WatchFaceHostApi {
+        private val coroutineScope = CoroutineScope(getHandler().asCoroutineDispatcher())
         private val _context = this@WatchFaceService as Context
 
         internal lateinit var iWatchFaceService: IWatchFaceService
@@ -321,11 +325,11 @@
         private lateinit var interactiveInstanceId: String
 
         init {
-            maybeCreateWCSApi()
+            coroutineScope.launch { maybeCreateWCSApi() }
         }
 
         @SuppressWarnings("NewApi")
-        private fun maybeCreateWCSApi() {
+        private suspend fun maybeCreateWCSApi() {
             val pendingWallpaperInstance =
                 InteractiveInstanceManager.takePendingWallpaperInteractiveWatchFaceInstance()
 
@@ -738,13 +742,13 @@
                 Log.w(TAG, "Failed to getVersion: ", e)
             }
 
-            maybeCreateWatchFace()
+            coroutineScope.launch { maybeCreateWatchFace() }
         }
 
         override fun getInitialUserStyle(): UserStyleWireFormat? = initialUserStyle
 
         @RequiresApi(27)
-        fun createHeadlessInstance(
+        suspend fun createHeadlessInstance(
             params: HeadlessWatchFaceInstanceParams
         ): HeadlessWatchFaceImpl {
             require(!watchFaceCreated())
@@ -823,7 +827,7 @@
 
         @UiThread
         @RequiresApi(27)
-        fun createInteractiveInstance(
+        suspend fun createInteractiveInstance(
             params: WallpaperInteractiveWatchFaceInstanceParams
         ): InteractiveWatchFaceImpl {
             require(!watchFaceCreated())
@@ -859,7 +863,7 @@
             }
         }
 
-        private fun maybeCreateWatchFace() {
+        private suspend fun maybeCreateWatchFace() {
             // To simplify handling of watch face state, we only construct the [WatchFaceImpl]
             // once iWatchFaceService have been initialized and pending properties sent.
             if (this::iWatchFaceService.isInitialized && pendingProperties != null &&
@@ -985,7 +989,7 @@
         internal fun onPropertiesChanged(properties: Bundle) {
             if (!watchFaceInitStarted) {
                 pendingProperties = properties
-                maybeCreateWatchFace()
+                coroutineScope.launch { maybeCreateWatchFace() }
                 return
             }
 
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
index 6b7c3ae..38df3e5 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
@@ -29,6 +29,7 @@
 import androidx.wear.watchface.control.data.HeadlessWatchFaceInstanceParams
 import androidx.wear.watchface.control.data.WallpaperInteractiveWatchFaceInstanceParams
 import androidx.wear.watchface.runOnHandler
+import kotlinx.coroutines.runBlocking
 
 /**
  * A service for creating and controlling watch face instances.
@@ -88,7 +89,11 @@
         params: HeadlessWatchFaceInstanceParams
     ): IHeadlessWatchFace? =
         uiThreadHandler.runOnHandler {
-            createEngine(params.watchFaceName, context)?.createHeadlessInstance(params)
+            val engine = createEngine(params.watchFaceName, context)
+            engine?.let {
+                // This is serviced on a background thread so it should be fine to block.
+                runBlocking { it.createHeadlessInstance(params) }
+            }
         }
 
     private fun createEngine(
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
index ae550c7..cf00ca8 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
@@ -89,7 +89,7 @@
         attachBaseContext(ApplicationProvider.getApplicationContext())
     }
 
-    override fun createWatchFace(
+    override suspend fun createWatchFace(
         surfaceHolder: SurfaceHolder,
         watchState: WatchState
     ) = WatchFace(
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index 2805d19..c25408d 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -285,6 +285,19 @@
     private var looperTimeMillis = 0L
     private val pendingTasks = PriorityQueue<Task>()
 
+    /**
+     * Runs any pending DispatchedContinuation tasks.  Care should be taken to ensure there's
+     * not other tasks in the queue ahead of these or they won't get run.
+     */
+    private fun runPendingPostedDispatchedContinuationTasks() {
+        while (pendingTasks.isNotEmpty() &&
+            pendingTasks.peek()!!.runTimeMillis <= looperTimeMillis &&
+            pendingTasks.peek()!!.runnable.toString().startsWith("DispatchedContinuation")
+        ) {
+            pendingTasks.remove().runnable.run()
+        }
+    }
+
     private fun runPostedTasksFor(durationMillis: Long) {
         looperTimeMillis += durationMillis
         while (pendingTasks.isNotEmpty() &&
@@ -377,6 +390,10 @@
         engineWrapper = testWatchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper
         engineWrapper.onCreate(surfaceHolder)
 
+        // [WatchFaceService.createWatchFace] is a suspend function backed by a handler coroutine
+        // dispatcher. We need to execute posted tasks inorder for the engine to get created.
+        runPendingPostedDispatchedContinuationTasks()
+
         // The [SurfaceHolder] must be sent before binding.
         engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
         watchFaceImpl = engineWrapper.watchFaceImpl
@@ -411,6 +428,12 @@
                 putBoolean(Constants.PROPERTY_BURN_IN_PROTECTION, hasBurnInProtection)
             }
         )
+
+        // [WatchFaceService.createWatchFace] is a suspend function backed by a handler coroutine
+        // dispatcher. We need to execute posted tasks for the engine to get created. We assume this
+        // is the last call made before the test needs to do something with the watch face, so we
+        // force execution here.
+        runPendingPostedDispatchedContinuationTasks()
     }
 
     private fun sendRequestStyle() {
@@ -1012,6 +1035,8 @@
             listOf(leftComplication, rightComplication),
             UserStyleSchema(emptyList())
         )
+        // Flush pending tasks posted as a result of initEngine.
+        runPostedTasksFor(0)
         assertThat(complicationsManager.getBackgroundComplication()).isNull()
         engineWrapper.onDestroy()
 
@@ -1020,6 +1045,8 @@
             listOf(leftComplication),
             UserStyleSchema(emptyList())
         )
+        // Flush pending tasks posted as a result of initEngine.
+        runPostedTasksFor(0)
         assertThat(complicationsManager.getBackgroundComplication()).isNull()
         engineWrapper.onDestroy()
 
@@ -1057,6 +1084,9 @@
             UserStyleSchema(listOf(colorStyleSetting, watchHandStyleSetting))
         )
 
+        // Flush pending tasks posted as a result of initEngine.
+        runPostedTasksFor(0)
+
         val testRenderer2 = TestRenderer(
             surfaceHolder,
             userStyleRepository2,
diff --git a/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java b/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java
index 5acba49..0c85963 100644
--- a/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java
+++ b/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java
@@ -364,12 +364,13 @@
         mSidecarCompat.setExtensionCallback(listener);
         when(mSidecarCompat.mSidecar.getWindowLayoutInfo(any())).thenReturn(layoutInfo);
         View fakeView = mock(View.class);
+        Window fakeWindow = new TestWindow(mActivity, fakeView);
         doAnswer(invocation -> {
             View.OnAttachStateChangeListener stateChangeListener = invocation.getArgument(0);
+            fakeWindow.getAttributes().token = mock(IBinder.class);
             stateChangeListener.onViewAttachedToWindow(fakeView);
             return null;
         }).when(fakeView).addOnAttachStateChangeListener(any());
-        Window fakeWindow = new TestWindow(mActivity, fakeView);
         when(mActivity.getWindow()).thenReturn(fakeWindow);
 
         mSidecarCompat.onWindowLayoutChangeListenerAdded(mActivity);
diff --git a/window/window/src/test/java/androidx/window/ActivityTestUtil.java b/window/window/src/main/java/androidx/window/ActivityUtil.java
similarity index 67%
rename from window/window/src/test/java/androidx/window/ActivityTestUtil.java
rename to window/window/src/main/java/androidx/window/ActivityUtil.java
index 2d6d5d7..48839f20 100644
--- a/window/window/src/test/java/androidx/window/ActivityTestUtil.java
+++ b/window/window/src/main/java/androidx/window/ActivityUtil.java
@@ -19,13 +19,17 @@
 import android.app.Activity;
 import android.os.IBinder;
 
-import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
-public class ActivityTestUtil {
+final class ActivityUtil {
 
-    private ActivityTestUtil() { }
+    private ActivityUtil() {}
 
-    static IBinder getActivityWindowToken(@NonNull Activity activity) {
-        return activity.getWindow().getAttributes().token;
+    @Nullable
+    static IBinder getActivityWindowToken(@Nullable Activity activity) {
+        if (activity == null) {
+            return null;
+        }
+        return activity.getWindow() != null ? activity.getWindow().getAttributes().token : null;
     }
 }
diff --git a/window/window/src/main/java/androidx/window/SidecarCompat.java b/window/window/src/main/java/androidx/window/SidecarCompat.java
index 4e64397..f0f850c 100644
--- a/window/window/src/main/java/androidx/window/SidecarCompat.java
+++ b/window/window/src/main/java/androidx/window/SidecarCompat.java
@@ -16,6 +16,7 @@
 
 package androidx.window;
 
+import static androidx.window.ActivityUtil.getActivityWindowToken;
 import static androidx.window.ExtensionCompat.DEBUG;
 import static androidx.window.Version.VERSION_0_1;
 
@@ -38,6 +39,7 @@
 import androidx.window.sidecar.SidecarProvider;
 import androidx.window.sidecar.SidecarWindowLayoutInfo;
 
+import java.lang.ref.WeakReference;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
@@ -142,17 +144,19 @@
         if (windowToken != null) {
             register(windowToken, activity);
         } else {
-            FirstAttachAdapter attachAdapter = new FirstAttachAdapter(() -> {
-                IBinder token = getActivityWindowToken(activity);
-                register(token, activity);
-            });
+            FirstAttachAdapter attachAdapter = new FirstAttachAdapter(this, activity);
             activity.getWindow().getDecorView().addOnAttachStateChangeListener(attachAdapter);
         }
     }
 
-    private void register(IBinder windowToken, Activity activity) {
+    /**
+     * Register an {@link IBinder} token and an {@link Activity} so that the given
+     * {@link Activity} will receive updates when there is a new {@link WindowLayoutInfo}.
+     * @param windowToken for the given {@link Activity}.
+     * @param activity that is listening for changes of {@link WindowLayoutInfo}
+     */
+    void register(@NonNull IBinder windowToken, @NonNull Activity activity) {
         mWindowListenerRegisteredContexts.put(windowToken, activity);
-
         mSidecar.onWindowLayoutChangeListenerAdded(windowToken);
         mExtensionCallback.onWindowLayoutChanged(activity, getWindowLayoutInfo(activity));
     }
@@ -319,31 +323,41 @@
         }
     }
 
-    @Nullable
-    private IBinder getActivityWindowToken(Activity activity) {
-        return activity.getWindow() != null ? activity.getWindow().getAttributes().token : null;
-    }
-
     /**
      * An adapter that will run a callback when a window is attached and then be removed from the
      * listener set.
      */
     private static class FirstAttachAdapter implements View.OnAttachStateChangeListener {
 
-        private final Runnable mCallback;
+        private final SidecarCompat mSidecarCompat;
+        private final WeakReference<Activity> mActivityWeakReference;
 
-        FirstAttachAdapter(Runnable callback) {
-            mCallback = callback;
+        FirstAttachAdapter(SidecarCompat sidecarCompat, Activity activity) {
+            mSidecarCompat = sidecarCompat;
+            mActivityWeakReference = new WeakReference<>(activity);
         }
 
         @Override
         public void onViewAttachedToWindow(View view) {
-            mCallback.run();
             view.removeOnAttachStateChangeListener(this);
+            Activity activity = mActivityWeakReference.get();
+            IBinder token = getActivityWindowToken(activity);
+            if (activity == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "Unable to register activity since activity is missing");
+                }
+                return;
+            }
+            if (token == null) {
+                if (DEBUG) {
+                    Log.w(TAG, "Unable to register activity since the window token is missing");
+                }
+                return;
+            }
+            mSidecarCompat.register(token, activity);
         }
 
         @Override
-        public void onViewDetachedFromWindow(View view) {
-        }
+        public void onViewDetachedFromWindow(View view) { }
     }
 }
diff --git a/window/window/src/test/java/androidx/window/SidecarCompatUnitTest.java b/window/window/src/test/java/androidx/window/SidecarCompatUnitTest.java
index a7b9da2..31cba768 100644
--- a/window/window/src/test/java/androidx/window/SidecarCompatUnitTest.java
+++ b/window/window/src/test/java/androidx/window/SidecarCompatUnitTest.java
@@ -16,7 +16,7 @@
 
 package androidx.window;
 
-import static androidx.window.ActivityTestUtil.getActivityWindowToken;
+import static androidx.window.ActivityUtil.getActivityWindowToken;
 import static androidx.window.TestBoundsUtil.invalidFoldBounds;
 import static androidx.window.TestBoundsUtil.invalidHingeBounds;
 import static androidx.window.TestBoundsUtil.validFoldBound;