Merge "Rename UiSavedStateRegistry to SaveableStateRegistry" into androidx-main
diff --git a/activity/activity-ktx/build.gradle b/activity/activity-ktx/build.gradle
index dbb44df..e754ba6 100644
--- a/activity/activity-ktx/build.gradle
+++ b/activity/activity-ktx/build.gradle
@@ -30,10 +30,10 @@
     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("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0")
     api("androidx.savedstate:savedstate-ktx:1.1.0") {
         because 'Mirror activity dependency graph for -ktx artifacts'
     }
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index 8874463..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("androidx.lifecycle:lifecycle-runtime:2.3.0")
+    api("androidx.lifecycle:lifecycle-viewmodel:2.3.0")
     api("androidx.savedstate:savedstate:1.1.0")
-    api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-savedstate"))
+    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 30416f6..05e1d78 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -19,8 +19,8 @@
     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")
+    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)
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/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..5133519 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
@@ -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/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/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..07950c0 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)
@@ -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/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/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..b61ab7e 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -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;
   }
 
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index 2640b07..b61ab7e 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -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;
   }
 
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 2640b07..b61ab7e 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -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;
   }
 
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/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/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..6fb1477 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
@@ -460,7 +459,7 @@
                 .textFieldKeyboardModifier(manager)
 
             SimpleLayout(coreTextFieldModifier) {
-                Layout(emptyContent()) { _, constraints ->
+                Layout({ }) { _, constraints ->
                     TextFieldDelegate.layout(
                         state.textDelegate,
                         constraints,
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..01dd0b6 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
@@ -68,7 +68,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
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/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/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/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/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/api/current.txt b/compose/runtime/runtime/api/current.txt
index 833d1e5..9a7f3d7 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -79,8 +79,8 @@
   }
 
   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 {
@@ -597,7 +597,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 833d1e5..9a7f3d7 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -79,8 +79,8 @@
   }
 
   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 {
@@ -597,7 +597,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 07e1499..fe8de7f 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -80,8 +80,8 @@
   }
 
   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 {
@@ -624,7 +624,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/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/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/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/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/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/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..bf5d4bf 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
@@ -164,7 +164,7 @@
     }
 
     private fun performSetContent(composable: @Composable() () -> Unit) {
-        val surface = Surface.makeRasterN32Premul(testDisplaySize.width, testDisplaySize.height)!!
+        val surface = Surface.makeRasterN32Premul(testDisplaySize.width, testDisplaySize.height)
         val canvas = surface.canvas
         val owners = DesktopOwners(invalidate = {}).also {
             owners = it
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
index 1f05602..3c47455 100644
--- 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
@@ -32,7 +32,7 @@
     val density: Density = Density(1f, 1f),
     var desktopPlatform: DesktopPlatform = DesktopPlatform.Linux
 ) {
-    val surface = Surface.makeRasterN32Premul(width, height)!!
+    val surface = Surface.makeRasterN32Premul(width, height)
     val canvas = surface.canvas
     val owners = DesktopOwners(invalidate = {})
 
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/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/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/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/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/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/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/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..43f7cdc 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,12 @@
 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 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 +44,47 @@
 import java.awt.im.InputMethodRequests
 
 internal class ComposeLayer {
+    private var isDisposed = false
 
-    private var composition: Composition? = null
     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(
+        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 +103,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 +127,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 +140,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 +148,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 +157,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 +165,7 @@
             }
 
             override fun mouseMoved(event: MouseEvent) = events.post {
-                owners?.onMouseMoved(
+                owners.onMouseMoved(
                     (event.x * density.density).toInt(),
                     (event.y * density.density).toInt()
                 )
@@ -191,7 +173,7 @@
         })
         wrapped.addMouseWheelListener { event ->
             events.post {
-                owners?.onMouseScroll(
+                owners.onMouseScroll(
                     (event.x * density.density).toInt(),
                     (event.y * density.density).toInt(),
                     event.toComposeEvent()
@@ -200,25 +182,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 +210,41 @@
         }
     )
 
-    // 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()
+        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/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..5aa7f3d 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,9 +24,9 @@
 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
@@ -174,7 +174,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()
     }
 
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/platform/RenderingTestScope.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt
index 9f966fa..7d0b1c1 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
@@ -23,9 +23,11 @@
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Dispatchers
 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
 
 internal fun renderingTest(
     width: Int,
@@ -49,13 +51,11 @@
 ) {
     var currentTimeMillis = 0L
 
-    val frameDispatcher = FrameDispatcher(
-        onFrame = { onRender(it) },
-        framesPerSecond = { Float.POSITIVE_INFINITY },
-        nanoTime = { currentTimeMillis * 1_000_000 }
-    )
+    val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
+        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(
         invalidate = frameDispatcher::scheduleFrame
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/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 c6a9da6..a5b8a4a 100644
--- a/fragment/fragment-ktx/build.gradle
+++ b/fragment/fragment-ktx/build.gradle
@@ -36,10 +36,10 @@
     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("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0")
     api("androidx.savedstate:savedstate-ktx:1.1.0") {
         because 'Mirror fragment dependency graph for -ktx artifacts'
     }
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index db758e9..a31aa62 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -38,9 +38,9 @@
     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("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")
 
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/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/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;