Merge "Revert "Wire up animation scale in Dev Options with Compose animation"" into androidx-main
diff --git a/activity/activity-ktx/api/api_lint.ignore b/activity/activity-ktx/api/api_lint.ignore
index 85e5f3a..43509e8 100644
--- a/activity/activity-ktx/api/api_lint.ignore
+++ b/activity/activity-ktx/api/api_lint.ignore
@@ -1,6 +1,8 @@
 // Baseline format: 1.0
 MissingNullability: androidx.activity.ActivityViewModelLazyKt#viewModels(androidx.activity.ComponentActivity, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
     Missing nullability on method `viewModels` return
+MissingNullability: androidx.activity.ActivityViewModelLazyKt#viewModels(androidx.activity.ComponentActivity, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
+    Missing nullability on method `viewModels` return
 
 
 RegistrationName: androidx.activity.OnBackPressedDispatcherKt#addCallback(androidx.activity.OnBackPressedDispatcher, androidx.lifecycle.LifecycleOwner, boolean, kotlin.jvm.functions.Function1<? super androidx.activity.OnBackPressedCallback,kotlin.Unit>):
diff --git a/activity/activity-ktx/api/current.txt b/activity/activity-ktx/api/current.txt
index f844b35..0a94311 100644
--- a/activity/activity-ktx/api/current.txt
+++ b/activity/activity-ktx/api/current.txt
@@ -2,7 +2,8 @@
 package androidx.activity {
 
   public final class ActivityViewModelLazyKt {
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
   }
 
   public final class OnBackPressedDispatcherKt {
diff --git a/activity/activity-ktx/api/public_plus_experimental_current.txt b/activity/activity-ktx/api/public_plus_experimental_current.txt
index 5dff406..0ca1317 100644
--- a/activity/activity-ktx/api/public_plus_experimental_current.txt
+++ b/activity/activity-ktx/api/public_plus_experimental_current.txt
@@ -2,7 +2,8 @@
 package androidx.activity {
 
   public final class ActivityViewModelLazyKt {
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
   }
 
   public final class OnBackPressedDispatcherKt {
diff --git a/activity/activity-ktx/api/restricted_current.txt b/activity/activity-ktx/api/restricted_current.txt
index f844b35..0a94311 100644
--- a/activity/activity-ktx/api/restricted_current.txt
+++ b/activity/activity-ktx/api/restricted_current.txt
@@ -2,7 +2,8 @@
 package androidx.activity {
 
   public final class ActivityViewModelLazyKt {
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
   }
 
   public final class OnBackPressedDispatcherKt {
diff --git a/activity/activity-ktx/src/androidTest/java/androidx/activity/ActivityViewModelLazyTest.kt b/activity/activity-ktx/src/androidTest/java/androidx/activity/ActivityViewModelLazyTest.kt
index ac106f6..bfcd4e1 100644
--- a/activity/activity-ktx/src/androidTest/java/androidx/activity/ActivityViewModelLazyTest.kt
+++ b/activity/activity-ktx/src/androidTest/java/androidx/activity/ActivityViewModelLazyTest.kt
@@ -17,8 +17,14 @@
 package androidx.activity
 
 import android.os.Bundle
+import androidx.core.os.bundleOf
+import androidx.lifecycle.DEFAULT_ARGS_KEY
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.SavedStateViewModelFactory
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.MutableCreationExtras
 import androidx.test.annotation.UiThreadTest
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
@@ -39,6 +45,7 @@
         assertThat(activity.vm).isNotNull()
         assertThat(activity.factoryVM.prop).isEqualTo("activity")
         assertThat(activity.daggerPoorCopyVM.prop).isEqualTo("dagger")
+        assertThat(activity.savedStateViewModel.defaultValue).isEqualTo("value")
     }
 
     class TestActivity : ComponentActivity() {
@@ -46,16 +53,32 @@
         val factoryVM: TestFactorizedViewModel by viewModels { VMFactory("activity") }
         lateinit var injectedFactory: ViewModelProvider.Factory
         val daggerPoorCopyVM: TestDaggerViewModel by viewModels { injectedFactory }
+        val savedStateViewModel: TestSavedStateViewModel by viewModels(
+            extrasProducer = { defaultViewModelCreationExtras }
+        )
 
         override fun onCreate(savedInstanceState: Bundle?) {
             injectedFactory = VMFactory("dagger")
             super.onCreate(savedInstanceState)
         }
+
+        override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory {
+            return SavedStateViewModelFactory()
+        }
+
+        override fun getDefaultViewModelCreationExtras(): CreationExtras {
+            val extras = MutableCreationExtras(super.getDefaultViewModelCreationExtras())
+            extras[DEFAULT_ARGS_KEY] = bundleOf("test" to "value")
+            return extras
+        }
     }
 
     class TestViewModel : ViewModel()
     class TestFactorizedViewModel(val prop: String) : ViewModel()
     class TestDaggerViewModel(val prop: String) : ViewModel()
+    class TestSavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel() {
+        val defaultValue = savedStateHandle.get<String>("test")
+    }
 
     private class VMFactory(val prop: String) : ViewModelProvider.Factory {
         @Suppress("UNCHECKED_CAST")
diff --git a/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt b/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt
index bf64ade..b7985fc 100644
--- a/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt
+++ b/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt
@@ -21,6 +21,41 @@
 import androidx.lifecycle.ViewModelLazy
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelProvider.Factory
+import androidx.lifecycle.viewmodel.CreationExtras
+
+/**
+ * Returns a [Lazy] delegate to access the ComponentActivity's ViewModel, if [factoryProducer]
+ * is specified then [ViewModelProvider.Factory] returned by it will be used
+ * to create [ViewModel] first time.
+ *
+ * ```
+ * class MyComponentActivity : ComponentActivity() {
+ *     val viewmodel: MyViewModel by viewModels()
+ * }
+ * ```
+ *
+ * This property can be accessed only after the Activity is attached to the Application,
+ * and access prior to that will result in IllegalArgumentException.
+ */
+@Deprecated(
+    "Superseded by viewModels that takes a CreationExtras",
+    level = DeprecationLevel.HIDDEN
+)
+@MainThread
+public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
+    noinline factoryProducer: (() -> Factory)? = null
+): Lazy<VM> {
+    val factoryPromise = factoryProducer ?: {
+        defaultViewModelProviderFactory
+    }
+
+    return ViewModelLazy(
+        VM::class,
+        { viewModelStore },
+        factoryPromise,
+        { this.defaultViewModelCreationExtras }
+    )
+}
 
 /**
  * Returns a [Lazy] delegate to access the ComponentActivity's ViewModel, if [factoryProducer]
@@ -38,6 +73,7 @@
  */
 @MainThread
 public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
+    noinline extrasProducer: (() -> CreationExtras)? = null,
     noinline factoryProducer: (() -> Factory)? = null
 ): Lazy<VM> {
     val factoryPromise = factoryProducer ?: {
@@ -48,6 +84,6 @@
         VM::class,
         { viewModelStore },
         factoryPromise,
-        { this.defaultViewModelCreationExtras }
+        { extrasProducer?.invoke() ?: this.defaultViewModelCreationExtras }
     )
 }
diff --git a/ads/ads-identifier-common/src/main/java/androidx/ads/identifier/AdvertisingIdUtils.java b/ads/ads-identifier-common/src/main/java/androidx/ads/identifier/AdvertisingIdUtils.java
index ae660f2..93d1e63 100644
--- a/ads/ads-identifier-common/src/main/java/androidx/ads/identifier/AdvertisingIdUtils.java
+++ b/ads/ads-identifier-common/src/main/java/androidx/ads/identifier/AdvertisingIdUtils.java
@@ -66,7 +66,7 @@
      * <p>Only system-level providers will be returned.
      */
     @NonNull
-    @SuppressWarnings("MixedMutabilityReturnType")
+    @SuppressWarnings({"MixedMutabilityReturnType", "deprecation"})
     public static List<ServiceInfo> getAdvertisingIdProviderServices(
             @NonNull PackageManager packageManager) {
         Intent intent = new Intent(GET_AD_ID_ACTION);
@@ -89,6 +89,7 @@
         return systemLevelServiceInfos;
     }
 
+    @SuppressWarnings("deprecation")
     private static boolean isSystemByApplicationInfo(
             @NonNull String packageName, @NonNull PackageManager packageManager) {
         try {
@@ -123,6 +124,7 @@
      * package is found.
      */
     @Nullable
+    @SuppressWarnings("deprecation")
     public static ServiceInfo selectServiceByPriority(
             @NonNull List<ServiceInfo> serviceInfos, @NonNull PackageManager packageManager) {
         if (serviceInfos.isEmpty()) {
diff --git a/ads/ads-identifier-common/src/test/java/androidx/ads/identifier/AdvertisingIdUtilsTest.java b/ads/ads-identifier-common/src/test/java/androidx/ads/identifier/AdvertisingIdUtilsTest.java
index ace51c6..50f7933 100644
--- a/ads/ads-identifier-common/src/test/java/androidx/ads/identifier/AdvertisingIdUtilsTest.java
+++ b/ads/ads-identifier-common/src/test/java/androidx/ads/identifier/AdvertisingIdUtilsTest.java
@@ -128,6 +128,7 @@
         assertThat(serviceInfo).isNull();
     }
 
+    @SuppressWarnings("deprecation")
     private ServiceInfo createServiceInfo(String packageName, boolean requestHighPriority,
             long firstInstallTime) throws Exception {
         PackageInfo packageInfo = new PackageInfo();
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderManager.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderManager.java
index b503dd0..990e786 100644
--- a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderManager.java
+++ b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderManager.java
@@ -148,7 +148,7 @@
      * <p>This is achieved by looking up which activities can handle {@link #OPEN_SETTINGS_ACTION}
      * intent action.
      */
-    @SuppressWarnings("MixedMutabilityReturnType")
+    @SuppressWarnings({"MixedMutabilityReturnType", "deprecation"})
     private static Map<String, String> getOpenSettingsActivities(PackageManager packageManager) {
         Intent settingsIntent = new Intent(OPEN_SETTINGS_ACTION);
         List<ResolveInfo> settingsResolveInfos = packageManager.queryIntentActivities(
diff --git a/ads/ads-identifier-testing/src/main/java/androidx/ads/identifier/testing/MockPackageManagerHelper.java b/ads/ads-identifier-testing/src/main/java/androidx/ads/identifier/testing/MockPackageManagerHelper.java
index 88b5abc..4fb5e1f 100644
--- a/ads/ads-identifier-testing/src/main/java/androidx/ads/identifier/testing/MockPackageManagerHelper.java
+++ b/ads/ads-identifier-testing/src/main/java/androidx/ads/identifier/testing/MockPackageManagerHelper.java
@@ -64,6 +64,7 @@
     }
 
     /** Mocks the {@link PackageManager#queryIntentServices(Intent, int)}. */
+    @SuppressWarnings("deprecation")
     public void mockQueryGetAdIdServices(@NonNull List<ResolveInfo> resolveInfos) throws Exception {
         boolean supportMatchSystemOnly = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
         when(mMockPackageManager.queryIntentServices(hasAction(GET_AD_ID_ACTION),
@@ -85,6 +86,7 @@
     }
 
     /** Mocks the {@link PackageManager#queryIntentActivities(Intent, int)}. */
+    @SuppressWarnings("deprecation")
     public void mockQueryOpenSettingsActivities(@NonNull List<ResolveInfo> resolveInfos) {
         when(mMockPackageManager.queryIntentActivities(hasAction(OPEN_SETTINGS_ACTION), eq(0)))
                 .thenReturn(resolveInfos);
diff --git a/ads/ads-identifier/integration-tests/testapp/src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.java b/ads/ads-identifier/integration-tests/testapp/src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.java
index cb12f2c..2f575fd 100644
--- a/ads/ads-identifier/integration-tests/testapp/src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.java
+++ b/ads/ads-identifier/integration-tests/testapp/src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.java
@@ -102,6 +102,7 @@
     }
 
     /** Lists all the providers. */
+    @SuppressWarnings("deprecation")
     public void listProvider(View view) {
         TextView textView = findViewById(R.id.text);
         textView.setText("Services:\n");
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerRtlTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerRtlTest.java
index 3ff4329..ba59ede 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerRtlTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerRtlTest.java
@@ -18,6 +18,7 @@
 import android.app.Instrumentation;
 import android.os.Build;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -49,6 +50,7 @@
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     }
 
+    @FlakyTest
     @Test
     public void testHorizontalOffsetRtl() {
         AppCompatSpinnerTest.checkOffsetIsCorrect(mInstrumentation, mContainer, 200, false, true);
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
index b659955..eb24e477 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
@@ -31,7 +31,11 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @RequiresApi(21)
 public class PerfettoCapture(
-    unbundled: Boolean = Build.VERSION.SDK_INT in 21..28
+    /**
+     * Bundled is available above API 28, but we default to using unbundled as well on API 29, as
+     * ProcessStatsConfig.scan_all_processes_on_start isn't supported on the bundled version.
+     */
+    unbundled: Boolean = Build.VERSION.SDK_INT in 21..29
 ) {
 
     private val helper: PerfettoHelper = PerfettoHelper(unbundled)
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
index 64fd2ee..b1be5ed 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
@@ -20,7 +20,6 @@
 import android.util.Log
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
-import androidx.benchmark.Arguments
 import androidx.benchmark.Outputs
 import androidx.benchmark.Outputs.dateToFileName
 import androidx.benchmark.PropOverride
@@ -72,8 +71,8 @@
         iteration: Int? = null,
         block: () -> Unit
     ): String? {
-        // skip if can't capture Perfetto, or Cuttlefish + dryRun (traces aren't needed anyway)
-        if (Build.VERSION.SDK_INT < 21 || (Arguments.dryRunMode && !isAbiSupported())) {
+        // skip if Perfetto not supported, or on Cuttlefish (where tracing doesn't work)
+        if (Build.VERSION.SDK_INT < 21 || !isAbiSupported()) {
             block()
             return null // tracing not supported
         }
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt
index 3b96d05..3c92e79 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt
@@ -94,6 +94,9 @@
         target_buffer = 1,
         process_stats_config = ProcessStatsConfig(
             proc_stats_poll_ms = 10000,
+            // This flag appears to be unreliable on API 29 unbundled perfetto, so to avoid very
+            // frequent proc stats polling to name processes correctly, we currently use unbundled
+            // perfetto on API 29, even though the bundled version exists. (b/218668335)
             scan_all_processes_on_start = true
         )
     )
@@ -192,4 +195,4 @@
         }
     }
     return encode()
-}
\ No newline at end of file
+}
diff --git a/benchmark/benchmark-macro/lint-baseline.xml b/benchmark/benchmark-macro/lint-baseline.xml
deleted file mode 100644
index cde602f..0000000
--- a/benchmark/benchmark-macro/lint-baseline.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (7.1.0-beta02)" variant="all" version="7.1.0-beta02">
-
-    <issue
-        id="MissingTestSizeAnnotation"
-        message="Missing test size annotation"
-        errorLine1="    fun fixedApi24Cold() = validateFixedTrace("
-        errorLine2="        ~~~~~~~~~~~~~~">
-        <location
-            file="src/androidTest/java/androidx/benchmark/macro/perfetto/StartupTimingQueryTest.kt"
-            line="54"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="MissingTestSizeAnnotation"
-        message="Missing test size annotation"
-        errorLine1="    fun fixedApi24Warm() = validateFixedTrace("
-        errorLine2="        ~~~~~~~~~~~~~~">
-        <location
-            file="src/androidTest/java/androidx/benchmark/macro/perfetto/StartupTimingQueryTest.kt"
-            line="65"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="MissingTestSizeAnnotation"
-        message="Missing test size annotation"
-        errorLine1="    fun fixedApi24Hot() = validateFixedTrace("
-        errorLine2="        ~~~~~~~~~~~~~">
-        <location
-            file="src/androidTest/java/androidx/benchmark/macro/perfetto/StartupTimingQueryTest.kt"
-            line="76"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="MissingTestSizeAnnotation"
-        message="Missing test size annotation"
-        errorLine1="    fun fixedApi31Cold() = validateFixedTrace("
-        errorLine2="        ~~~~~~~~~~~~~~">
-        <location
-            file="src/androidTest/java/androidx/benchmark/macro/perfetto/StartupTimingQueryTest.kt"
-            line="87"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="MissingTestSizeAnnotation"
-        message="Missing test size annotation"
-        errorLine1="    fun fixedApi31Warm() = validateFixedTrace("
-        errorLine2="        ~~~~~~~~~~~~~~">
-        <location
-            file="src/androidTest/java/androidx/benchmark/macro/perfetto/StartupTimingQueryTest.kt"
-            line="98"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="MissingTestSizeAnnotation"
-        message="Missing test size annotation"
-        errorLine1="    fun fixedApi31Hot() = validateFixedTrace("
-        errorLine2="        ~~~~~~~~~~~~~">
-        <location
-            file="src/androidTest/java/androidx/benchmark/macro/perfetto/StartupTimingQueryTest.kt"
-            line="109"
-            column="9"/>
-    </issue>
-
-</issues>
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
index 4ad481e..9308615 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
@@ -41,6 +41,7 @@
     private val instrumentation = InstrumentationRegistry.getInstrumentation()
 
     @Before
+    @Suppress("DEPRECATION")
     fun setup() {
         // validate target is installed with clear error message,
         // since error messages from e.g. startActivityAndWait may be less clear
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/StartupTimingQueryTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/StartupTimingQueryTest.kt
index 51be679..b913cfa 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/StartupTimingQueryTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/StartupTimingQueryTest.kt
@@ -20,24 +20,24 @@
 import androidx.benchmark.macro.createTempFileFromAsset
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
 import org.junit.Assume.assumeTrue
 import org.junit.Test
 import org.junit.runner.RunWith
 import java.util.Locale
 import kotlin.test.assertEquals
 
+@MediumTest
 @RunWith(AndroidJUnit4::class)
 class StartupTimingQueryTest {
     private fun validateFixedTrace(
         @Suppress("SameParameterValue") api: Int,
         startupMode: StartupMode,
-        expectedMetrics: StartupTimingQuery.SubMetrics
+        expectedMetrics: StartupTimingQuery.SubMetrics?,
+        tracePrefix: String = "api${api}_startup_${startupMode.name.lowercase(Locale.getDefault())}"
     ) {
         assumeTrue(isAbiSupported())
-        val traceFile = createTempFileFromAsset(
-            prefix = "api${api}_startup_${startupMode.name.lowercase(Locale.getDefault())}",
-            suffix = ".perfetto-trace"
-        )
+        val traceFile = createTempFileFromAsset(prefix = tracePrefix, suffix = ".perfetto-trace")
 
         val startupSubMetrics = StartupTimingQuery.getFrameSubMetrics(
             absoluteTracePath = traceFile.absolutePath,
@@ -115,4 +115,15 @@
             timelineRangeNs = 186969441973689..186969984196243
         )
     )
-}
\ No newline at end of file
+
+    /**
+     * Validate that StartupTimingQuery returns null and doesn't crash when process name truncated
+     */
+    @Test
+    fun fixedApi29ColdProcessNameTruncated() = validateFixedTrace(
+        api = 29,
+        startupMode = StartupMode.COLD,
+        tracePrefix = "api29_cold_startup_processname_truncated",
+        expectedMetrics = null // process name is truncated, and we currently don't handle this
+    )
+}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
index 0dd14cf..28a5952 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
@@ -23,6 +23,9 @@
 import androidx.annotation.RestrictTo
 import androidx.benchmark.DeviceInfo
 import androidx.benchmark.Shell
+import androidx.benchmark.macro.CompilationMode.Full
+import androidx.benchmark.macro.CompilationMode.None
+import androidx.benchmark.macro.CompilationMode.Partial
 import androidx.profileinstaller.ProfileInstallReceiver
 import androidx.profileinstaller.ProfileInstaller
 import org.junit.AssumptionViolatedException
@@ -65,10 +68,78 @@
         if (Build.VERSION.SDK_INT >= 24) {
             Log.d(TAG, "Clearing profiles for $packageName")
             Shell.executeCommand("cmd package compile --reset $packageName")
+            writeProfileInstallerSkipFile(packageName)
             compileImpl(packageName, warmupBlock)
         }
     }
 
+    /**
+     * Writes a skip file via a [ProfileInstallReceiver] broadcast, so profile installation
+     * does not interfere with benchmarks.
+     */
+    private fun writeProfileInstallerSkipFile(packageName: String) {
+        val result = profileInstallerSkipFileOperation(packageName, "WRITE_SKIP_FILE")
+        if (result != null) {
+            Log.w(
+                TAG,
+                """
+                    $packageName should use the latest version of `androidx.profileinstaller`
+                    for stable benchmarks. ($result)"
+                """.trimIndent()
+            )
+        }
+    }
+
+    /**
+     * Uses skip files for avoiding interference from ProfileInstaller when using
+     * [CompilationMode.None].
+     *
+     * Operation name is one of `WRITE_SKIP_FILE` or `DELETE_SKIP_FILE`.
+     *
+     * Returned error strings aren't thrown, to let the calling function decide strictness.
+     */
+    private fun profileInstallerSkipFileOperation(
+        packageName: String,
+        operation: String
+    ): String? {
+        // Redefining constants here, because these are only defined in the latest alpha for
+        // ProfileInstaller.
+
+        // Use an explicit broadcast given the app was force-stopped.
+        val name = ProfileInstallReceiver::class.java.name
+        val action = "androidx.profileinstaller.action.SKIP_FILE"
+        val operationKey = "EXTRA_SKIP_FILE_OPERATION"
+        val extras = "$operationKey $operation"
+        Log.d(TAG, "Profile Installation Skip File Operation: $operation")
+        val result = Shell.executeCommand("am broadcast -a $action -e $extras $packageName/$name")
+            .substringAfter("Broadcast completed: result=")
+            .trim()
+            .toIntOrNull()
+        return when {
+            result == null || result == 0 -> {
+                // 0 is returned by the platform by default, and also if no broadcast receiver
+                // receives the broadcast.
+
+                "The baseline profile skip file broadcast was not received. " +
+                    "This most likely means that the `androidx.profileinstaller` library " +
+                    "used by the target apk is old. Please use `1.2.0-alpha03` or newer. " +
+                    "For more information refer to the release notes at " +
+                    "https://developer.android.com/jetpack/androidx/releases/profileinstaller."
+            }
+            operation == "WRITE_SKIP_FILE" && result == 10 -> { // RESULT_INSTALL_SKIP_FILE_SUCCESS
+                null // success!
+            }
+            operation == "DELETE_SKIP_FILE" && result == 11 -> { // RESULT_DELETE_SKIP_FILE_SUCCESS
+                null // success!
+            }
+            else -> {
+                throw RuntimeException(
+                    "unrecognized ProfileInstaller result code: $result"
+                )
+            }
+        }
+    }
+
     @RequiresApi(24)
     internal fun cmdPackageCompile(packageName: String, compileArgument: String) {
         Shell.executeCommand("cmd package compile -f -m $compileArgument $packageName")
@@ -207,6 +278,7 @@
 
         override fun compileImpl(packageName: String, warmupBlock: () -> Unit) {
             if (baselineProfileMode != BaselineProfileMode.Disable) {
+                // Ignores the presence of a skip file.
                 val installErrorString = broadcastBaselineProfileInstall(packageName)
                 if (installErrorString == null) {
                     // baseline profile install success, kill process before compiling
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index 9265979..aca7f19 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -38,6 +38,7 @@
 import androidx.tracing.trace
 import java.io.File
 
+@Suppress("DEPRECATION")
 internal fun checkErrors(packageName: String): ConfigurationError.SuppressionState? {
     val pm = InstrumentationRegistry.getInstrumentation().context.packageManager
 
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
index 5103fa2..bf07e48 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
@@ -16,8 +16,10 @@
 
 package androidx.benchmark.macro.perfetto
 
+import androidx.benchmark.macro.perfetto.PerfettoTraceProcessor.processNameLikePkg
+
 internal object FrameTimingQuery {
-    private fun getFullQuery(processName: String) = """
+    private fun getFullQuery(packageName: String) = """
         ------ Select all frame-relevant slices from slice table
         SELECT
             slice.name as name,
@@ -30,7 +32,7 @@
         WHERE (
             ( slice.name LIKE "Choreographer#doFrame%" AND process.pid LIKE thread.tid ) OR
             ( slice.name LIKE "DrawFrame%" AND thread.name like "RenderThread" )
-        ) AND (process.name LIKE "$processName")
+        ) AND ${processNameLikePkg(packageName)}
         ------ Add in actual frame slices (prepended with "actual " to differentiate)
         UNION
         SELECT
@@ -40,7 +42,7 @@
         FROM actual_frame_timeline_slice
             INNER JOIN process USING(upid)
         WHERE
-            process.name LIKE "$processName"
+            ${processNameLikePkg(packageName)}
         ------ Add in expected time slices (prepended with "expected " to differentiate)
         UNION
         SELECT
@@ -50,7 +52,7 @@
         FROM expected_frame_timeline_slice
             INNER JOIN process USING(upid)
         WHERE
-            process.name LIKE "$processName"
+            ${processNameLikePkg(packageName)}
         ORDER BY ts ASC
     """.trimIndent()
 
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt
index b94b9a1..66a570b 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoTraceProcessor.kt
@@ -114,4 +114,11 @@
             queryFile.delete()
         }
     }
+
+    /**
+     * Helper for fuzzy matching process name to package
+     */
+    internal fun processNameLikePkg(pkg: String): String {
+        return """(process.name LIKE "$pkg" OR process.name LIKE "$pkg:%")"""
+    }
 }
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/StartupTimingQuery.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/StartupTimingQuery.kt
index 368b6d9..a31965c 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/StartupTimingQuery.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/StartupTimingQuery.kt
@@ -16,11 +16,13 @@
 
 package androidx.benchmark.macro.perfetto
 
+import android.util.Log
 import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.perfetto.PerfettoTraceProcessor.processNameLikePkg
 
 internal object StartupTimingQuery {
 
-    private fun getFullQuery(testProcessName: String, targetProcessName: String) = """
+    private fun getFullQuery(testPackageName: String, targetPackageName: String) = """
         ------ Select all startup-relevant slices from slice table
         SELECT
             slice.name as name,
@@ -31,9 +33,9 @@
             INNER JOIN thread USING(utid)
             INNER JOIN process USING(upid)
         WHERE (
-            (process.name LIKE "$testProcessName" AND slice.name LIKE "startActivityAndWait") OR
+            (${processNameLikePkg(testPackageName)} AND slice.name LIKE "startActivityAndWait") OR
             (
-                process.name LIKE "$targetProcessName" AND (
+                ${processNameLikePkg(targetPackageName)} AND (
                     (slice.name LIKE "activityResume" AND process.pid LIKE thread.tid) OR
                     (slice.name LIKE "Choreographer#doFrame%" AND process.pid LIKE thread.tid) OR
                     (slice.name LIKE "reportFullyDrawn() for %" AND process.pid LIKE thread.tid) OR
@@ -114,8 +116,8 @@
         val queryResult = PerfettoTraceProcessor.rawQuery(
             absoluteTracePath = absoluteTracePath,
             query = getFullQuery(
-                testProcessName = testPackageName,
-                targetProcessName = targetPackageName
+                testPackageName = testPackageName,
+                targetPackageName = targetPackageName
             )
         )
         val slices = Slice.parseListFromQueryResult(queryResult)
@@ -144,6 +146,11 @@
         val uiSlices = groupedData.getOrElse(StartupSliceType.FrameUiThread) { listOf() }
         val rtSlices = groupedData.getOrElse(StartupSliceType.FrameRenderThread) { listOf() }
 
+        if (uiSlices.isEmpty() || rtSlices.isEmpty()) {
+            Log.d("Benchmark", "No UI / RT slices seen, not reporting startup.")
+            return null
+        }
+
         val startTs: Long
         val initialDisplayTs: Long
         if (captureApiLevel >= 29 || startupMode != StartupMode.HOT) {
diff --git a/benchmark/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/benchmark/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
index 69c5a3b..b742f04 100644
--- a/benchmark/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/benchmark/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -71,16 +71,23 @@
             </intent-filter>
         </activity>
 
-        <!--
-        Activities need to be exported so the macrobenchmark can discover them
-         -->
         <activity
             android:name=".NotExportedActivity"
-            android:exported="false">
+            android:exported="false"> <!-- intentionally not exported -->
             <intent-filter>
                 <action android:name="androidx.benchmark.integration.macrobenchmark.target.NOT_EXPORTED_ACTIVITY" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+
+        <activity
+            android:name=".SeparateProcessActivity"
+            android:process=":ui"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="androidx.benchmark.integration.macrobenchmark.target.SEPARATE_PROCESS_ACTIVITY" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/SeparateProcessActivity.kt b/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/SeparateProcessActivity.kt
new file mode 100644
index 0000000..830679e
--- /dev/null
+++ b/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/SeparateProcessActivity.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.benchmark.integration.macrobenchmark.target
+
+import android.os.Bundle
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+
+/**
+ * Trivial activity which lives in a separate ":ui" process
+ */
+class SeparateProcessActivity : AppCompatActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_main)
+
+        val notice = findViewById<TextView>(R.id.txtNotice)
+        notice.setText(R.string.app_notice)
+    }
+}
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/SeparateProcessBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/SeparateProcessBenchmark.kt
new file mode 100644
index 0000000..2b4d47c
--- /dev/null
+++ b/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/SeparateProcessBenchmark.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark.integration.macrobenchmark
+
+import android.content.Intent
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.StartupTimingMetric
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Validates metrics coming from an app's sub-process.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class SeparateProcessBenchmark {
+    @get:Rule
+    val benchmarkRule = MacrobenchmarkRule()
+
+    @Test
+    fun startup() = benchmarkRule.measureRepeated(
+        compilationMode = CompilationMode.DEFAULT,
+        startupMode = StartupMode.COLD,
+        packageName = "androidx.benchmark.integration.macrobenchmark.target",
+        metrics = listOf(StartupTimingMetric(), FrameTimingMetric()),
+        iterations = 2
+    ) {
+        startActivityAndWait(Intent("$packageName.SEPARATE_PROCESS_ACTIVITY"))
+    }
+}
diff --git a/benchmark/integration-tests/test-module-sample/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialTestModuleTest.kt b/benchmark/integration-tests/test-module-sample/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialTestModuleTest.kt
index 5f84e75..aac7497 100644
--- a/benchmark/integration-tests/test-module-sample/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialTestModuleTest.kt
+++ b/benchmark/integration-tests/test-module-sample/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialTestModuleTest.kt
@@ -49,6 +49,7 @@
 
     @Ignore // b/202321897
     @Test
+    @Suppress("DEPRECATION")
     fun targetPackageInstalled() {
         val pm = InstrumentationRegistry.getInstrumentation().context.packageManager
         try {
diff --git a/browser/browser/src/main/java/androidx/browser/browseractions/BrowserActionsIntent.java b/browser/browser/src/main/java/androidx/browser/browseractions/BrowserActionsIntent.java
index 3e80ac84..7ea9202 100644
--- a/browser/browser/src/main/java/androidx/browser/browseractions/BrowserActionsIntent.java
+++ b/browser/browser/src/main/java/androidx/browser/browseractions/BrowserActionsIntent.java
@@ -348,6 +348,7 @@
     /** @hide */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @VisibleForTesting
+    @SuppressWarnings("deprecation")
     static void launchIntent(Context context, Intent intent, List<ResolveInfo> handlers) {
         if (handlers == null || handlers.size() == 0) {
             openFallbackBrowserActionsMenu(context, intent);
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java
index edf8144..a597247 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java
@@ -138,6 +138,7 @@
      * @param ignoreDefault If set, the default VIEW handler won't get priority over other browsers.
      * @return The preferred package name for handling Custom Tabs, or <code>null</code>.
      */
+    @SuppressWarnings("deprecation")
     public static @Nullable String getPackageName(
             @NonNull Context context, @Nullable List<String> packages, boolean ignoreDefault) {
         PackageManager pm = context.getPackageManager();
diff --git a/browser/browser/src/main/java/androidx/browser/trusted/PackageIdentityUtils.java b/browser/browser/src/main/java/androidx/browser/trusted/PackageIdentityUtils.java
index b554dbd..7900b85 100644
--- a/browser/browser/src/main/java/androidx/browser/trusted/PackageIdentityUtils.java
+++ b/browser/browser/src/main/java/androidx/browser/trusted/PackageIdentityUtils.java
@@ -77,6 +77,7 @@
     }
 
     @RequiresApi(28)
+    @SuppressWarnings("deprecation")
     static class Api28Implementation implements SignaturesCompat {
         @Override
         @Nullable
diff --git a/browser/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityService.java b/browser/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityService.java
index b62d039..e0630fc 100644
--- a/browser/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityService.java
+++ b/browser/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityService.java
@@ -339,6 +339,7 @@
      * @return A resource id for the small icon, or {@link #SMALL_ICON_NOT_SET} if not found.
      */
     @BinderThread
+    @SuppressWarnings("deprecation")
     public int onGetSmallIconId() {
         try {
             ServiceInfo info = getPackageManager().getServiceInfo(
diff --git a/browser/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceConnectionPool.java b/browser/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceConnectionPool.java
index a7438ef..31d2cd4 100644
--- a/browser/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceConnectionPool.java
+++ b/browser/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceConnectionPool.java
@@ -205,6 +205,7 @@
      * {@code possiblePackages}.
      * Will return {@code null} if there is no applicable Service.
      */
+    @SuppressWarnings("deprecation")
     private @Nullable Intent createServiceIntent(Context appContext, Uri scope,
             Set<Token> possiblePackages, boolean shouldLog) {
         if (possiblePackages == null || possiblePackages.size() == 0) {
diff --git a/camera/camera-camera2/src/main/AndroidManifest.xml b/camera/camera-camera2/src/main/AndroidManifest.xml
index a5795e3..ff5826d 100644
--- a/camera/camera-camera2/src/main/AndroidManifest.xml
+++ b/camera/camera-camera2/src/main/AndroidManifest.xml
@@ -22,7 +22,7 @@
             android:name="androidx.camera.core.impl.MetadataHolderService"
             android:exported="false"
             android:enabled="false"
-            tools:ignore="Instantiatable"
+            tools:ignore="Instantiatable,MissingServiceExportedEqualsTrue"
             tools:node="merge">
             <meta-data
                 android:name=
diff --git a/camera/camera-core/src/main/AndroidManifest.xml b/camera/camera-core/src/main/AndroidManifest.xml
index d41ce9b..1656645 100644
--- a/camera/camera-core/src/main/AndroidManifest.xml
+++ b/camera/camera-core/src/main/AndroidManifest.xml
@@ -24,6 +24,6 @@
             android:name="androidx.camera.core.impl.MetadataHolderService"
             android:enabled="false"
             android:exported="false"
-            tools:ignore="Instantiatable" />
+            tools:ignore="Instantiatable,MissingServiceExportedEqualsTrue" />
     </application>
 </manifest>
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 00a7812..3c3ac31 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
@@ -152,6 +152,7 @@
     }
 
     @Nullable
+    @SuppressWarnings("deprecation")
     private static CameraXConfig.Provider getConfigProvider(@NonNull Context context) {
         CameraXConfig.Provider configProvider = null;
         Application application = ContextUtil.getApplicationFromContext(context);
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
index c9d826a..509c110 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
@@ -183,7 +183,7 @@
     @Test
     fun correctAvailability_whenExtensionIsNotAvailable() {
         // Skips the test if extensions availability is disabled by quirk.
-        assumeFalse(ExtensionsTestUtil.extensionsDisabledByQuirk())
+        assumeFalse(ExtensionsTestUtil.extensionsDisabledByQuirk(lensFacing, extensionMode))
 
         extensionsManager = ExtensionsManager.getInstanceAsync(
             context,
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
index e93b482..d171589 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
@@ -209,13 +209,17 @@
     /**
      * Returns whether extensions is disabled by quirk.
      */
-    public static boolean extensionsDisabledByQuirk() {
+    public static boolean extensionsDisabledByQuirk(@CameraSelector.LensFacing int lensFacing,
+            @ExtensionMode.Mode int extensionMode) {
+
         boolean isAdvancedExtenderSupported = false;
 
         if (ExtensionVersion.getRuntimeVersion().compareTo(Version.VERSION_1_2) >= 0) {
             isAdvancedExtenderSupported = ExtensionVersion.isAdvancedExtenderSupported();
         }
 
-        return new ExtensionDisabledValidator().shouldDisableExtension(isAdvancedExtenderSupported);
+        return new ExtensionDisabledValidator().shouldDisableExtension(
+                CameraUtil.getCameraIdWithLensFacing(lensFacing), extensionMode,
+                isAdvancedExtenderSupported);
     }
 }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsInfo.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsInfo.java
index 918b6d5..4204cc3 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsInfo.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsInfo.java
@@ -44,7 +44,6 @@
 import androidx.camera.extensions.internal.ExtensionsUseCaseConfigFactory;
 import androidx.camera.extensions.internal.VendorExtender;
 import androidx.camera.extensions.internal.Version;
-import androidx.camera.extensions.internal.compat.workaround.ExtensionDisabledValidator;
 
 import java.util.Collections;
 import java.util.List;
@@ -64,8 +63,6 @@
 final class ExtensionsInfo {
     private static final String EXTENDED_CAMERA_CONFIG_PROVIDER_ID_PREFIX = ":camera:camera"
             + "-extensions-";
-    private static final ExtensionDisabledValidator sExtensionDisabledValidator =
-            new ExtensionDisabledValidator();
     private final CameraProvider mCameraProvider;
 
     ExtensionsInfo(@NonNull CameraProvider cameraProvider) {
@@ -240,11 +237,6 @@
             return new DisabledVendorExtender();
         }
 
-        // Force disable extension for some devices by quirk.
-        if (sExtensionDisabledValidator.shouldDisableExtension(isAdvancedExtenderSupported)) {
-            return new DisabledVendorExtender();
-        }
-
         VendorExtender vendorExtender;
         if (isAdvancedExtenderSupported) {
             vendorExtender = new AdvancedVendorExtender(mode);
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
index 08a60e7..831af2d 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
@@ -38,6 +38,7 @@
 import androidx.camera.extensions.impl.advanced.BokehAdvancedExtenderImpl;
 import androidx.camera.extensions.impl.advanced.HdrAdvancedExtenderImpl;
 import androidx.camera.extensions.impl.advanced.NightAdvancedExtenderImpl;
+import androidx.camera.extensions.internal.compat.workaround.ExtensionDisabledValidator;
 import androidx.camera.extensions.internal.sessionprocessor.AdvancedSessionProcessor;
 import androidx.core.util.Preconditions;
 
@@ -51,10 +52,14 @@
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class AdvancedVendorExtender implements VendorExtender {
+    private final ExtensionDisabledValidator mExtensionDisabledValidator =
+            new ExtensionDisabledValidator();
     private final AdvancedExtenderImpl mAdvancedExtenderImpl;
     private String mCameraId;
+    private final @ExtensionMode.Mode int mMode;
 
     public AdvancedVendorExtender(@ExtensionMode.Mode int mode) {
+        mMode = mode;
         try {
             switch (mode) {
                 case ExtensionMode.BOKEH:
@@ -95,6 +100,11 @@
     @Override
     public boolean isExtensionAvailable(@NonNull String cameraId,
             @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
+
+        if (mExtensionDisabledValidator.shouldDisableExtension(cameraId, mMode, true)) {
+            return false;
+        }
+
         return mAdvancedExtenderImpl.isExtensionAvailable(cameraId, characteristicsMap);
     }
 
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
index d42b2c5..ca27b15 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
@@ -49,6 +49,7 @@
 import androidx.camera.extensions.impl.NightPreviewExtenderImpl;
 import androidx.camera.extensions.impl.PreviewExtenderImpl;
 import androidx.camera.extensions.impl.ProcessorImpl;
+import androidx.camera.extensions.internal.compat.workaround.ExtensionDisabledValidator;
 import androidx.core.util.Preconditions;
 
 import java.util.Arrays;
@@ -61,6 +62,8 @@
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class BasicVendorExtender implements VendorExtender {
     private static final String TAG = "BasicVendorExtender";
+    private final ExtensionDisabledValidator mExtensionDisabledValidator =
+            new ExtensionDisabledValidator();
     private final @ExtensionMode.Mode int mMode;
     private PreviewExtenderImpl mPreviewExtenderImpl;
     private ImageCaptureExtenderImpl mImageCaptureExtenderImpl;
@@ -122,6 +125,11 @@
     @Override
     public boolean isExtensionAvailable(@NonNull String cameraId,
             @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
+
+        if (mExtensionDisabledValidator.shouldDisableExtension(cameraId, mMode, false)) {
+            return false;
+        }
+
         CameraCharacteristics cameraCharacteristics = characteristicsMap.get(cameraId);
         return mPreviewExtenderImpl.isExtensionAvailable(cameraId, cameraCharacteristics)
                 && mImageCaptureExtenderImpl.isExtensionAvailable(cameraId, cameraCharacteristics);
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java
index 7447218..19a8e0f 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java
@@ -18,30 +18,51 @@
 
 import android.os.Build;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.impl.Quirk;
+import androidx.camera.extensions.ExtensionMode;
+
 
 /**
  * Quirk required to disable extension for some devices.
  *
- * <p>An example is Pixel 5 which the availability check result of the basic extension interface
- * face should be false, but it actually return true. Therefore, a default VendorExtender will
- * be used to return false availability check result. See b/199408131.
+ * <p>An example is that Pixel 5's availability check result of the basic extension
+ * interface should be false, but it actually returns true. Therefore, force disable Basic
+ * Extender capability on the device. See b/199408131.
+ *
+ * <p>Another example is that Motorola razr 5G's availability check results of both back
+ * and front camera are true, but it will cause the black preview screen issue. Therefore, force
+ * disable the bokeh mode on the device. See b/214130117.
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class ExtensionDisabledQuirk implements Quirk {
     static boolean load() {
-        return isPixel5();
+        return isPixel5() || isMotoRazr5G();
     }
 
     /**
      * Checks whether extension should be disabled.
      */
-    public boolean shouldDisableExtension(boolean isAdvancedExtenderSupported) {
-        return !isAdvancedExtenderSupported && isPixel5();
+    public boolean shouldDisableExtension(@NonNull String cameraId,
+            @ExtensionMode.Mode int extensionMode, boolean isAdvancedInterface) {
+        if (isPixel5() && !isAdvancedInterface) {
+            // 1. Disables Pixel 5's Basic Extender capability.
+            return true;
+        } else if (isMotoRazr5G() && ("0".equals(cameraId) || "1".equals(cameraId)) && (
+                ExtensionMode.BOKEH == extensionMode)) {
+            // 2. Disables Motorola Razr 5G's bokeh capability.
+            return true;
+        }
+
+        return false;
     }
 
     private static boolean isPixel5() {
         return "google".equalsIgnoreCase(Build.BRAND) && "redfin".equalsIgnoreCase(Build.DEVICE);
     }
+
+    private static boolean isMotoRazr5G() {
+        return "motorola".equalsIgnoreCase(Build.BRAND) && "smith".equalsIgnoreCase(Build.DEVICE);
+    }
 }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java
index d920317..ff7403e 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java
@@ -16,16 +16,19 @@
 
 package androidx.camera.extensions.internal.compat.workaround;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
+import androidx.camera.extensions.ExtensionMode;
 import androidx.camera.extensions.internal.compat.quirk.DeviceQuirks;
 import androidx.camera.extensions.internal.compat.quirk.ExtensionDisabledQuirk;
 
 /**
- * Validates whether extension should be disabled.
+ * Validates whether the specified extension mode should be disabled for the specified camera on
+ * the device.
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class ExtensionDisabledValidator {
-    private ExtensionDisabledQuirk mQuirk;
+    private final ExtensionDisabledQuirk mQuirk;
 
     /**
      * Constructs an instance of {@link ExtensionDisabledValidator}.
@@ -35,9 +38,11 @@
     }
 
     /**
-     *  Checks whether extension should be disabled.
+     * Checks whether extension should be disabled.
      */
-    public boolean shouldDisableExtension(boolean isAdvancedExtenderSupported) {
-        return mQuirk == null ? false : mQuirk.shouldDisableExtension(isAdvancedExtenderSupported);
+    public boolean shouldDisableExtension(@NonNull String cameraId,
+            @ExtensionMode.Mode int extensionMode, boolean isAdvancedInterface) {
+        return mQuirk != null && mQuirk.shouldDisableExtension(cameraId, extensionMode,
+                isAdvancedInterface);
     }
 }
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/quirk/DeviceQuirks.java b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/quirk/DeviceQuirks.java
new file mode 100644
index 0000000..8d61c48
--- /dev/null
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/quirk/DeviceQuirks.java
@@ -0,0 +1,59 @@
+/*
+ * 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.camera.extensions.internal.compat.quirk;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.camera.core.impl.Quirk;
+
+import java.util.List;
+
+/**
+ * Tests version of main/.../DeviceQuirks.java, which provides device specific quirks, used for
+ * device specific workarounds.
+ * <p>
+ * In main/.../DeviceQuirks, Device quirks are loaded the first time a device workaround is
+ * encountered, and remain in memory until the process is killed. When running tests, this means
+ * that the same device quirks are used for all the tests. This causes an issue when tests modify
+ * device properties (using Robolectric for instance). Instead of force-reloading the device
+ * quirks in every test that uses a device workaround, this class internally reloads the quirks
+ * every time a device workaround is needed.
+ */
+public class DeviceQuirks {
+
+    private DeviceQuirks() {
+    }
+
+    /**
+     * Retrieves a specific device {@link Quirk} instance given its type.
+     *
+     * @param quirkClass The type of device quirk to retrieve.
+     * @return A device {@link Quirk} instance of the provided type, or {@code null} if it isn't
+     * found.
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public static <T extends Quirk> T get(@NonNull final Class<T> quirkClass) {
+        final List<Quirk> quirks = DeviceQuirksLoader.loadQuirks();
+        for (final Quirk quirk : quirks) {
+            if (quirk.getClass() == quirkClass) {
+                return (T) quirk;
+            }
+        }
+        return null;
+    }
+}
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt
index 4203a6b..2f4ddb0 100644
--- a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidatorTest.kt
@@ -17,6 +17,7 @@
 package androidx.camera.extensions.internal.compat.workaround
 
 import android.os.Build
+import androidx.camera.extensions.ExtensionMode
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -29,24 +30,29 @@
 @DoNotInstrument
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 class ExtensionDisabledValidatorTest(private val config: TestConfig) {
-    @Test
-    fun shouldUseDefaultVendorExtender() {
-        // Set up device properties
-        if (config.brand != null) {
-            ReflectionHelpers.setStaticField(Build::class.java, "BRAND", config.brand)
-            ReflectionHelpers.setStaticField(Build::class.java, "DEVICE", config.device)
-        }
 
-        val validator =
-            ExtensionDisabledValidator()
-        assertThat(validator.shouldDisableExtension(config.isAdvancedExtenderSupported))
-            .isEqualTo(config.shouldDisableExtension)
+    @Test
+    fun shouldDisableExtensionMode() {
+        // Set up device properties
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", config.brand)
+        ReflectionHelpers.setStaticField(Build::class.java, "DEVICE", config.device)
+
+        val validator = ExtensionDisabledValidator()
+        assertThat(
+            validator.shouldDisableExtension(
+                config.cameraId,
+                config.extensionMode,
+                config.isAdvancedInterface
+            )
+        ).isEqualTo(config.shouldDisableExtension)
     }
 
     class TestConfig(
-        val brand: String?,
-        val device: String?,
-        val isAdvancedExtenderSupported: Boolean,
+        val brand: String,
+        val device: String,
+        val cameraId: String,
+        val extensionMode: Int,
+        val isAdvancedInterface: Boolean,
         val shouldDisableExtension: Boolean
     )
 
@@ -55,10 +61,33 @@
         @ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
         fun createTestSet(): List<TestConfig> {
             return listOf(
-                TestConfig("Google", "Redfin", false, true),
-                TestConfig("Google", "Redfin", true, false),
-                TestConfig("", "", false, false),
-                TestConfig("", "", true, false)
+                // Pixel 5 extension capability is disabled on basic extender
+                TestConfig("Google", "Redfin", "0", ExtensionMode.BOKEH, false, true),
+                TestConfig("Google", "Redfin", "0", ExtensionMode.HDR, false, true),
+                TestConfig("Google", "Redfin", "0", ExtensionMode.NIGHT, false, true),
+                TestConfig("Google", "Redfin", "0", ExtensionMode.FACE_RETOUCH, false, true),
+                TestConfig("Google", "Redfin", "0", ExtensionMode.AUTO, false, true),
+                TestConfig("Google", "Redfin", "1", ExtensionMode.BOKEH, false, true),
+                TestConfig("Google", "Redfin", "1", ExtensionMode.HDR, false, true),
+                TestConfig("Google", "Redfin", "1", ExtensionMode.NIGHT, false, true),
+                TestConfig("Google", "Redfin", "1", ExtensionMode.FACE_RETOUCH, false, true),
+                TestConfig("Google", "Redfin", "1", ExtensionMode.AUTO, false, true),
+
+                // Pixel 5 extension capability is not disabled on advanced extender
+                TestConfig("Google", "Redfin", "0", ExtensionMode.NIGHT, true, false),
+                TestConfig("Google", "Redfin", "1", ExtensionMode.NIGHT, true, false),
+
+                // Motorola Razr 5G bokeh mode is disabled. Other extension modes should still work.
+                TestConfig("Motorola", "Smith", "0", ExtensionMode.BOKEH, false, true),
+                TestConfig("Motorola", "Smith", "0", ExtensionMode.HDR, false, false),
+                TestConfig("Motorola", "Smith", "1", ExtensionMode.BOKEH, false, true),
+                TestConfig("Motorola", "Smith", "1", ExtensionMode.HDR, false, false),
+                TestConfig("Motorola", "Smith", "2", ExtensionMode.BOKEH, false, false),
+                TestConfig("Motorola", "Smith", "2", ExtensionMode.HDR, false, false),
+
+                // Other cases should be kept normal.
+                TestConfig("", "", "0", ExtensionMode.BOKEH, false, false),
+                TestConfig("", "", "1", ExtensionMode.BOKEH, false, false)
             )
         }
     }
diff --git a/camera/camera-previewview/src/main/java/androidx/camera/previewview/internal/quirk/QuirkSummary.java b/camera/camera-previewview/src/main/java/androidx/camera/previewview/internal/quirk/QuirkSummary.java
new file mode 100644
index 0000000..3779367
--- /dev/null
+++ b/camera/camera-previewview/src/main/java/androidx/camera/previewview/internal/quirk/QuirkSummary.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.previewview.internal.quirk;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+@Documented
+@Retention(CLASS)
+public @interface QuirkSummary {
+}
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
index de8cebe..d49133e 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
@@ -466,6 +466,7 @@
     }
 
     /** Tries to acquire all the necessary permissions through a dialog. */
+    @SuppressWarnings("deprecation")
     private String[] getRequiredPermissions() {
         PackageInfo info;
         try {
diff --git a/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/TestUtils.kt b/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/TestUtils.kt
index 72e3e10..bc16441 100644
--- a/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/TestUtils.kt
+++ b/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/TestUtils.kt
@@ -610,7 +610,8 @@
 /**
  * Return the version name of the Activity
  */
+@Suppress("DEPRECATION")
 fun getVersionName(activity: MainActivity): String {
     val packageInfo = activity.packageManager.getPackageInfo(activity.packageName, 0)
     return packageInfo.versionName
-}
\ No newline at end of file
+}
diff --git a/car/app/app-automotive/src/main/AndroidManifest.xml b/car/app/app-automotive/src/main/AndroidManifest.xml
index 43b025b..6f5812b 100644
--- a/car/app/app-automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-automotive/src/main/AndroidManifest.xml
@@ -32,7 +32,7 @@
             android:exported="false"
             android:enabled="false"
             android:process=""
-            tools:ignore="Instantiatable"
+            tools:ignore="Instantiatable,MissingServiceExportedEqualsTrue"
             tools:node="merge">
             <meta-data
                 android:name=
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
index e53a8b8..913ae84 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
@@ -419,11 +419,14 @@
     @Override
     protected void onDestroy() {
         requireNonNull(mHostUpdateReceiver).unregister(this);
+        requireNonNull(mSurfaceHolderListener).setSurfaceListener(null);
+        requireNonNull(mViewModel).unbind();
         requireNonNull(mViewModel).setActivity(null);
         super.onDestroy();
     }
 
     @Nullable
+    @SuppressWarnings("deprecation")
     private ComponentName retrieveServiceComponentName() {
         Intent intent = new Intent(SERVICE_INTERFACE);
         intent.setPackage(getPackageName());
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
index 5b3c684..28cce46 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
@@ -132,6 +132,7 @@
     void unbind() {
         mServiceConnectionManager.unbind();
         mIInsetsListener = null;
+        mIRendererCallback = null;
     }
 
     @Override
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
index 4eaa1d2..8d91a334 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
@@ -185,6 +185,7 @@
      * Initializes the renderer service with given properties if already bound to the renderer
      * service.
      */
+    @SuppressWarnings("deprecation")
     void bind(@NonNull Intent intent, @NonNull ICarAppActivity iCarAppActivity, int displayId) {
         mIntent = requireNonNull(intent);
         mICarAppActivity = requireNonNull(iCarAppActivity);
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/ErrorMessageView.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/ErrorMessageView.java
index 7c6e9e7..331537b 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/ErrorMessageView.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/ErrorMessageView.java
@@ -135,6 +135,7 @@
         throw new IllegalArgumentException("Unknown action type: " + mErrorType.getActionType());
     }
 
+    @SuppressWarnings("deprecation")
     private boolean isVendingPackageInstalled() {
         try {
             requireActivity().getPackageManager().getPackageInfo(VENDING_PACKAGE, 0);
@@ -145,6 +146,7 @@
         return true;
     }
 
+    @SuppressWarnings("deprecation")
     private Intent getVendingIntent() {
         Intent rendererIntent = new Intent(ACTION_RENDER);
         List<ResolveInfo> resolveInfoList =
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/LoadingView.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/LoadingView.java
index a41b574..9467648 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/LoadingView.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ui/LoadingView.java
@@ -66,6 +66,7 @@
         mAppIcon.setImageDrawable(getActivityIcon());
     }
 
+    @SuppressWarnings("deprecation")
     private Drawable getActivityIcon() {
         PackageManager packageManager = getContext().getPackageManager();
 
diff --git a/car/app/app-projected/src/main/AndroidManifest.xml b/car/app/app-projected/src/main/AndroidManifest.xml
index 186a4fd..82385a2 100644
--- a/car/app/app-projected/src/main/AndroidManifest.xml
+++ b/car/app/app-projected/src/main/AndroidManifest.xml
@@ -24,7 +24,7 @@
             android:exported="false"
             android:enabled="false"
             android:process=""
-            tools:ignore="Instantiatable"
+            tools:ignore="Instantiatable,MissingServiceExportedEqualsTrue"
             tools:node="merge">
             <meta-data
                 android:name=
diff --git a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarHardwareHostDispatcherTest.java b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarHardwareHostDispatcherTest.java
index 041b16b..006edc6 100644
--- a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarHardwareHostDispatcherTest.java
+++ b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarHardwareHostDispatcherTest.java
@@ -121,7 +121,7 @@
     @Test
     public void dispatchUnsubscribeCarHardwareResult() throws RemoteException, BundlerException {
         int desiredResultType = ICarHardwareResultTypes.TYPE_SENSOR_ACCELEROMETER;
-        Bundleable bundle = Bundleable.create(new Integer(10));
+        Bundleable bundle = Bundleable.create(10);
         mCarHardwareHostDispatcher.dispatchUnsubscribeCarHardwareResult(desiredResultType, bundle);
         verify(mMockCarHardwareHost).unsubscribeCarHardwareResult(eq(desiredResultType),
                 eq(bundle));
diff --git a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubMapTest.java b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubMapTest.java
index 4d0faa7..04e9656 100644
--- a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubMapTest.java
+++ b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubMapTest.java
@@ -92,7 +92,7 @@
         Integer desiredResult = 5;
         Bundleable desiredBundleable = Bundleable.create(desiredResult);
         int desiredResultType = ICarHardwareResultTypes.TYPE_SENSOR_ACCELEROMETER;
-        Integer unsupportedResult = new Integer(-1);
+        Integer unsupportedResult = -1;
         String param = "param";
         Bundleable paramBundle = Bundleable.create(param);
 
diff --git a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubTest.java b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubTest.java
index e70fba4..7215449 100644
--- a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubTest.java
+++ b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubTest.java
@@ -77,7 +77,7 @@
         Integer desiredResult = 5;
         Bundleable desiredBundleable = Bundleable.create(desiredResult);
         int desiredResultType = ICarHardwareResultTypes.TYPE_INFO_MODEL;
-        Integer unsupportedResult = new Integer(-1);
+        Integer unsupportedResult = -1;
 
         String param = "param";
         Bundleable paramBundle = Bundleable.create(param);
@@ -98,7 +98,7 @@
     public void addListener_callHost_unsupported_singleShot() throws BundlerException,
             RemoteException {
         int desiredResultType = ICarHardwareResultTypes.TYPE_INFO_MODEL;
-        Integer unsupportedResult = new Integer(-1);
+        Integer unsupportedResult = -1;
 
         String param = "param";
         Bundleable paramBundle = Bundleable.create(param);
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/RequestPermissionScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/RequestPermissionScreen.java
index ad6e272..7f9e70d 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/RequestPermissionScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/RequestPermissionScreen.java
@@ -79,6 +79,7 @@
 
     @NonNull
     @Override
+    @SuppressWarnings("deprecation")
     public Template onGetTemplate() {
         final Action headerAction = mPreSeedMode ? Action.APP_ICON : Action.BACK;
         List<String> permissions = new ArrayList<>();
diff --git a/car/app/app-testing/src/test/java/androidx/car/app/testing/TestScreenManagerTest.java b/car/app/app-testing/src/test/java/androidx/car/app/testing/TestScreenManagerTest.java
index 97c6352..30c9974 100644
--- a/car/app/app-testing/src/test/java/androidx/car/app/testing/TestScreenManagerTest.java
+++ b/car/app/app-testing/src/test/java/androidx/car/app/testing/TestScreenManagerTest.java
@@ -22,10 +22,10 @@
 import androidx.car.app.Screen;
 import androidx.car.app.ScreenManager;
 import androidx.car.app.model.Template;
+import androidx.lifecycle.Lifecycle;
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -40,6 +40,10 @@
     @Before
     public void setup() {
         mCarContext = TestCarContext.createCarContext(ApplicationProvider.getApplicationContext());
+        // Set the app's lifecycle to STARTED so that screens would be set to the STARTED state when
+        // pushed. Otherwise we may run into issues in tests when popping screens, where they go
+        // from the INITIALIZED state to the DESTROYED state.
+        mCarContext.getLifecycleOwner().getRegistry().setCurrentState(Lifecycle.State.STARTED);
     }
 
     @Test
@@ -65,7 +69,6 @@
                 .containsExactly(screen1, screen2);
     }
 
-    @Ignore("b/218342974")
     @Test
     public void pop_getScreensRemoved() {
         Screen screen1 = new TestScreen();
@@ -83,7 +86,6 @@
                 .containsExactly(screen4);
     }
 
-    @Ignore("b/218342974")
     @Test
     public void remove_getScreensRemoved() {
         Screen screen1 = new TestScreen();
@@ -101,7 +103,6 @@
                 .containsExactly(screen2);
     }
 
-    @Ignore("b/218342974")
     @Test
     public void popTo_getScreensRemoved() {
         Screen screen1 = new TestScreen();
@@ -122,7 +123,6 @@
                 .containsExactly(screen4, screen3);
     }
 
-    @Ignore("b/218342974")
     @Test
     public void popToRoot_getScreensRemoved() {
         Screen screen1 = new TestScreen();
diff --git a/car/app/app/src/main/java/androidx/car/app/AppInfo.java b/car/app/app/src/main/java/androidx/car/app/AppInfo.java
index 1db3946..d74fc85 100644
--- a/car/app/app/src/main/java/androidx/car/app/AppInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/AppInfo.java
@@ -129,6 +129,7 @@
     @RestrictTo(Scope.LIBRARY)
     @VisibleForTesting
     @CarAppApiLevel
+    @SuppressWarnings("deprecation")
     public static int retrieveMinCarAppApiLevel(@NonNull Context context) {
         try {
             ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(
diff --git a/car/app/app/src/main/java/androidx/car/app/CarAppMetadataHolderService.java b/car/app/app/src/main/java/androidx/car/app/CarAppMetadataHolderService.java
index f85125b..799e4c7 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarAppMetadataHolderService.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarAppMetadataHolderService.java
@@ -53,7 +53,7 @@
      * Returns the {@link ServiceInfo} for the declared {@link CarAppMetadataHolderService}.
      */
     @NonNull
-    @SuppressWarnings("deprecation") // GET_DISABLED_COMPONENTS
+    @SuppressWarnings("deprecation") // GET_DISABLED_COMPONENTS, getServiceInfo
     public static ServiceInfo getServiceInfo(@NonNull Context context) throws
             PackageManager.NameNotFoundException {
         int flags = PackageManager.GET_META_DATA;
diff --git a/car/app/app/src/main/java/androidx/car/app/CarAppPermission.java b/car/app/app/src/main/java/androidx/car/app/CarAppPermission.java
index 9349bb1..22913a2 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarAppPermission.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarAppPermission.java
@@ -104,6 +104,7 @@
      *
      * @throws SecurityException if the app does not have the required permission declared
      */
+    @SuppressWarnings("deprecation")
     public static void checkHasLibraryPermission(
             @NonNull Context context, @NonNull @LibraryPermission String permission) {
         if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/car/app/app/src/main/java/androidx/car/app/CarAppPermissionActivity.java b/car/app/app/src/main/java/androidx/car/app/CarAppPermissionActivity.java
index 664f64a..e759e55 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarAppPermissionActivity.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarAppPermissionActivity.java
@@ -55,6 +55,7 @@
         processInternal(getIntent());
     }
 
+    @SuppressWarnings("deprecation")
     private void maybeSetCustomBackground() {
         @StyleRes int themeId = Resources.ID_NULL;
         ApplicationInfo applicationInfo;
diff --git a/car/app/app/src/main/java/androidx/car/app/notification/CarNotificationManager.java b/car/app/app/src/main/java/androidx/car/app/notification/CarNotificationManager.java
index e94496c..2a9c643 100644
--- a/car/app/app/src/main/java/androidx/car/app/notification/CarNotificationManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/notification/CarNotificationManager.java
@@ -434,6 +434,7 @@
     }
 
     @StyleRes
+    @SuppressWarnings("deprecation")
     private static int loadThemeId(Context context) {
         int theme = Resources.ID_NULL;
         ApplicationInfo applicationInfo;
diff --git a/car/app/app/src/main/java/androidx/car/app/notification/CarPendingIntent.java b/car/app/app/src/main/java/androidx/car/app/notification/CarPendingIntent.java
index f321557..0c98298 100644
--- a/car/app/app/src/main/java/androidx/car/app/notification/CarPendingIntent.java
+++ b/car/app/app/src/main/java/androidx/car/app/notification/CarPendingIntent.java
@@ -119,6 +119,7 @@
      * @see CarContext#startCarApp(Intent)
      */
     @VisibleForTesting
+    @SuppressWarnings("deprecation")
     static void validateIntent(Context context, Intent intent) {
         String packageName = context.getPackageName();
         String action = intent.getAction();
diff --git a/car/app/app/src/main/java/androidx/car/app/validation/HostValidator.java b/car/app/app/src/main/java/androidx/car/app/validation/HostValidator.java
index 2d52162..bb8fcd2 100644
--- a/car/app/app/src/main/java/androidx/car/app/validation/HostValidator.java
+++ b/car/app/app/src/main/java/androidx/car/app/validation/HostValidator.java
@@ -336,6 +336,7 @@
 
         @DoNotInline
         @NonNull
+        @SuppressWarnings("deprecation")
         static PackageInfo getPackageInfo(@NonNull PackageManager packageManager,
                 @NonNull String packageName) throws PackageManager.NameNotFoundException {
             return packageManager.getPackageInfo(packageName,
diff --git a/car/app/app/src/test/java/androidx/car/app/AppInfoTest.java b/car/app/app/src/test/java/androidx/car/app/AppInfoTest.java
index da37284..a7f9235 100644
--- a/car/app/app/src/test/java/androidx/car/app/AppInfoTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/AppInfoTest.java
@@ -48,6 +48,7 @@
     private final ApplicationInfo mApplicationInfo = new ApplicationInfo();
 
     @Before
+    @SuppressWarnings("deprecation")
     public void setUp() throws PackageManager.NameNotFoundException {
         MockitoAnnotations.initMocks(this);
 
diff --git a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
index 6a74ac5..1883180 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
@@ -53,7 +53,6 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -129,6 +128,10 @@
                 ApplicationProvider.getApplicationContext(),
                 ApplicationProvider.getApplicationContext().getResources().getConfiguration());
         mCarContext.setCarHost(mMockCarHost);
+        // Set the app's lifecycle to STARTED so that screens would be set to the STARTED state when
+        // pushed. Otherwise we may run into issues in tests when popping screens, where they go
+        // from the INITIALIZED state to the DESTROYED state.
+        mLifecycleOwner.mRegistry.setCurrentState(State.STARTED);
 
         mScreen1 = new TestScreen(mCarContext, mMockScreen1);
         mScreen2 = new TestScreen(mCarContext, mMockScreen2);
@@ -363,7 +366,6 @@
         }
     }
 
-    @Ignore("b/218342974")
     @Test
     public void getOnBackPressedDispatcher_noListeners_popsAScreen() {
         mCarContext.getCarService(ScreenManager.class).push(mScreen1);
@@ -382,9 +384,10 @@
 
         OnBackPressedCallback callback = mock(OnBackPressedCallback.class);
         when(callback.isEnabled()).thenReturn(true);
-        mLifecycleOwner.mRegistry.setCurrentState(State.STARTED);
 
-        mCarContext.getOnBackPressedDispatcher().addCallback(mLifecycleOwner, callback);
+        TestLifecycleOwner callbackLifecycle = new TestLifecycleOwner();
+        callbackLifecycle.mRegistry.setCurrentState(State.STARTED);
+        mCarContext.getOnBackPressedDispatcher().addCallback(callbackLifecycle, callback);
         mCarContext.getOnBackPressedDispatcher().onBackPressed();
 
         verify(callback).handleOnBackPressed();
@@ -392,7 +395,6 @@
         verify(mMockScreen2, never()).dispatchLifecycleEvent(Event.ON_DESTROY);
     }
 
-    @Ignore("b/218342974")
     @Test
     public void getOnBackPressedDispatcher_withAListenerThatIsNotStarted_popsAScreen() {
         mCarContext.getCarService(ScreenManager.class).push(mScreen1);
@@ -400,9 +402,10 @@
 
         OnBackPressedCallback callback = mock(OnBackPressedCallback.class);
         when(callback.isEnabled()).thenReturn(true);
-        mLifecycleOwner.mRegistry.setCurrentState(State.CREATED);
 
-        mCarContext.getOnBackPressedDispatcher().addCallback(mLifecycleOwner, callback);
+        TestLifecycleOwner callbackLifecycle = new TestLifecycleOwner();
+        callbackLifecycle.mRegistry.setCurrentState(State.CREATED);
+        mCarContext.getOnBackPressedDispatcher().addCallback(callbackLifecycle, callback);
         mCarContext.getOnBackPressedDispatcher().onBackPressed();
 
         verify(callback, never()).handleOnBackPressed();
@@ -410,7 +413,6 @@
         verify(mMockScreen2).dispatchLifecycleEvent(Event.ON_DESTROY);
     }
 
-    @Ignore("b/218342974")
     @Test
     public void getOnBackPressedDispatcher_callsDefaultListenerWheneverTheAddedOneIsNotSTARTED() {
         mCarContext.getCarService(ScreenManager.class).push(mScreen1);
@@ -418,15 +420,16 @@
 
         OnBackPressedCallback callback = mock(OnBackPressedCallback.class);
         when(callback.isEnabled()).thenReturn(true);
-        mLifecycleOwner.mRegistry.setCurrentState(State.CREATED);
 
-        mCarContext.getOnBackPressedDispatcher().addCallback(mLifecycleOwner, callback);
+        TestLifecycleOwner callbackLifecycle = new TestLifecycleOwner();
+        callbackLifecycle.mRegistry.setCurrentState(State.CREATED);
+        mCarContext.getOnBackPressedDispatcher().addCallback(callbackLifecycle, callback);
         mCarContext.getOnBackPressedDispatcher().onBackPressed();
 
         verify(callback, never()).handleOnBackPressed();
         verify(mMockScreen2).dispatchLifecycleEvent(Event.ON_DESTROY);
 
-        mLifecycleOwner.mRegistry.setCurrentState(State.STARTED);
+        callbackLifecycle.mRegistry.setCurrentState(State.STARTED);
         mCarContext.getOnBackPressedDispatcher().onBackPressed();
 
         verify(callback).handleOnBackPressed();
diff --git a/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTestApi28.java b/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTestApi28.java
index b56b1e1..cdff5ce 100644
--- a/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTestApi28.java
+++ b/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTestApi28.java
@@ -100,6 +100,7 @@
         assertThat(hostValidator.isValidHost(hostInfo)).isTrue();
     }
 
+    @SuppressWarnings("deprecation")
     private void installPackage(String packageName, Signature[] signatures) {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.applicationInfo = new ApplicationInfo();
diff --git a/compose/foundation/foundation/api/current.ignore b/compose/foundation/foundation/api/current.ignore
index 2633800..42d5f19 100644
--- a/compose/foundation/foundation/api/current.ignore
+++ b/compose/foundation/foundation/api/current.ignore
@@ -5,6 +5,8 @@
     Removed class androidx.compose.foundation.lazy.layout.LazyLayoutStateKt
 RemovedClass: androidx.compose.foundation.lazy.list.IntervalListKt:
     Removed class androidx.compose.foundation.lazy.list.IntervalListKt
+RemovedClass: androidx.compose.foundation.relocation.BringRectangleOnScreen_androidKt:
+    Removed class androidx.compose.foundation.relocation.BringRectangleOnScreen_androidKt
 RemovedClass: androidx.compose.foundation.text.TextFieldMagnifierKt:
     Removed class androidx.compose.foundation.text.TextFieldMagnifierKt
 RemovedClass: androidx.compose.foundation.text.TextFieldMagnifier_androidKt:
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index f5a4468..defd30f 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -548,13 +548,16 @@
 
 package androidx.compose.foundation.relocation {
 
+  public final class BringIntoViewKt {
+  }
+
   public final class BringIntoViewRequesterKt {
   }
 
   public final class BringIntoViewResponderKt {
   }
 
-  public final class BringRectangleOnScreen_androidKt {
+  public final class BringIntoViewResponder_androidKt {
   }
 
 }
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index 27cb31c..a5ad4a7 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -690,6 +690,9 @@
 
 package androidx.compose.foundation.relocation {
 
+  public final class BringIntoViewKt {
+  }
+
   @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface BringIntoViewRequester {
     method public suspend Object? bringIntoView(optional androidx.compose.ui.geometry.Rect? rect, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
   }
@@ -700,22 +703,15 @@
   }
 
   @androidx.compose.foundation.ExperimentalFoundationApi public interface BringIntoViewResponder {
-    method public suspend Object? bringIntoView(androidx.compose.ui.geometry.Rect rect, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method public androidx.compose.ui.geometry.Rect toLocalRect(androidx.compose.ui.geometry.Rect rect, androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates);
-    field public static final androidx.compose.foundation.relocation.BringIntoViewResponder.Companion Companion;
-  }
-
-  @androidx.compose.foundation.ExperimentalFoundationApi public static final class BringIntoViewResponder.Companion {
-    method public androidx.compose.ui.modifier.ProvidableModifierLocal<androidx.compose.foundation.relocation.BringIntoViewResponder> getModifierLocalBringIntoViewResponder();
-    method public androidx.compose.foundation.relocation.BringIntoViewResponder getRootBringIntoViewResponder();
-    property public final androidx.compose.ui.modifier.ProvidableModifierLocal<androidx.compose.foundation.relocation.BringIntoViewResponder> ModifierLocalBringIntoViewResponder;
-    property public final androidx.compose.foundation.relocation.BringIntoViewResponder RootBringIntoViewResponder;
+    method @androidx.compose.foundation.ExperimentalFoundationApi public suspend Object? bringChildIntoView(androidx.compose.ui.geometry.Rect localRect, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.geometry.Rect calculateRectForParent(androidx.compose.ui.geometry.Rect localRect);
   }
 
   public final class BringIntoViewResponderKt {
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier bringIntoViewResponder(androidx.compose.ui.Modifier, androidx.compose.foundation.relocation.BringIntoViewResponder responder);
   }
 
-  public final class BringRectangleOnScreen_androidKt {
+  public final class BringIntoViewResponder_androidKt {
   }
 
 }
diff --git a/compose/foundation/foundation/api/restricted_current.ignore b/compose/foundation/foundation/api/restricted_current.ignore
index 2633800..42d5f19 100644
--- a/compose/foundation/foundation/api/restricted_current.ignore
+++ b/compose/foundation/foundation/api/restricted_current.ignore
@@ -5,6 +5,8 @@
     Removed class androidx.compose.foundation.lazy.layout.LazyLayoutStateKt
 RemovedClass: androidx.compose.foundation.lazy.list.IntervalListKt:
     Removed class androidx.compose.foundation.lazy.list.IntervalListKt
+RemovedClass: androidx.compose.foundation.relocation.BringRectangleOnScreen_androidKt:
+    Removed class androidx.compose.foundation.relocation.BringRectangleOnScreen_androidKt
 RemovedClass: androidx.compose.foundation.text.TextFieldMagnifierKt:
     Removed class androidx.compose.foundation.text.TextFieldMagnifierKt
 RemovedClass: androidx.compose.foundation.text.TextFieldMagnifier_androidKt:
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index f5a4468..defd30f 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -548,13 +548,16 @@
 
 package androidx.compose.foundation.relocation {
 
+  public final class BringIntoViewKt {
+  }
+
   public final class BringIntoViewRequesterKt {
   }
 
   public final class BringIntoViewResponderKt {
   }
 
-  public final class BringRectangleOnScreen_androidKt {
+  public final class BringIntoViewResponder_androidKt {
   }
 
 }
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt
index a21353b..cb41b94 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt
@@ -18,8 +18,12 @@
 
 import androidx.compose.foundation.demos.relocation.BringIntoViewAndroidInteropDemo
 import androidx.compose.foundation.demos.relocation.BringIntoViewDemo
+import androidx.compose.foundation.demos.relocation.BringIntoViewResponderDemo
+import androidx.compose.foundation.demos.relocation.BringNestedIntoViewDemo
 import androidx.compose.foundation.demos.relocation.BringRectangleIntoViewDemo
 import androidx.compose.foundation.demos.relocation.RequestRectangleOnScreenDemo
+import androidx.compose.foundation.samples.BringIntoViewResponderSample
+import androidx.compose.foundation.samples.BringPartOfComposableIntoViewSample
 import androidx.compose.foundation.samples.ControlledScrollableRowSample
 import androidx.compose.foundation.samples.InteractionSourceFlowSample
 import androidx.compose.foundation.samples.SimpleInteractionSourceSample
@@ -29,9 +33,13 @@
 
 private val RelocationDemos = listOf(
     ComposableDemo("Bring Into View") { BringIntoViewDemo() },
+    /** This gives [BringPartOfComposableIntoViewSample] some explanation text. */
     ComposableDemo("Bring Rectangle Into View") { BringRectangleIntoViewDemo() },
+    /** This gives [BringIntoViewResponderSample] some explanation text. */
+    ComposableDemo("Custom responder") { BringIntoViewResponderDemo() },
     ComposableDemo("Request Rectangle On Screen") { RequestRectangleOnScreenDemo() },
-    ComposableDemo("Android view interop") { BringIntoViewAndroidInteropDemo() }
+    ComposableDemo("Android view interop") { BringIntoViewAndroidInteropDemo() },
+    ComposableDemo("Nested scrollables") { BringNestedIntoViewDemo() },
 )
 
 val FoundationDemos = DemoCategory(
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/BringNestedIntoViewDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/BringNestedIntoViewDemo.kt
new file mode 100644
index 0000000..cb26929
--- /dev/null
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/BringNestedIntoViewDemo.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("SameParameterValue")
+
+package androidx.compose.foundation.demos.relocation
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.relocation.BringIntoViewRequester
+import androidx.compose.foundation.relocation.bringIntoViewRequester
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.IconButton
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.launch
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun BringNestedIntoViewDemo() {
+    Column {
+        val rows = 3
+        val columns = 3
+        val bringIntoViewRequesters = remember { List(rows * columns) { BringIntoViewRequester() } }
+
+        Text(
+            "This is a $rows x $columns grid of circles. The entire grid is vertically " +
+                "scrollable, and each row in the grid is horizontally scrollable. Click the " +
+                "buttons in the smaller grid below to bring the corresponding circle into view."
+        )
+
+        ScrollableGrid(rows, columns, bringIntoViewRequesters)
+        ControlGrid(rows, columns, bringIntoViewRequesters)
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun ScrollableGrid(rows: Int, columns: Int, requesters: List<BringIntoViewRequester>) {
+    Column(
+        Modifier
+            .border(3.dp, Color.Blue)
+            .size(200.dp, 250.dp)
+            .verticalScroll(rememberScrollState())
+    ) {
+        repeat(rows) { row ->
+            Row(
+                Modifier
+                    // Inner scrollable rows.
+                    .border(3.dp, Color.Green)
+                    .fillMaxWidth()
+                    .height(200.dp)
+                    .horizontalScroll(rememberScrollState())
+                    .width(600.dp),
+                horizontalArrangement = Arrangement.SpaceAround,
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                // Circles
+                repeat(columns) { column ->
+                    val index = row * columns + column
+                    TextCircle(
+                        index.toString(),
+                        Modifier
+                            .size(75.dp)
+                            .bringIntoViewRequester(requesters[index])
+                    )
+                }
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun ControlGrid(rows: Int, columns: Int, requesters: List<BringIntoViewRequester>) {
+    val coroutineScope = rememberCoroutineScope()
+    Column {
+        repeat(rows) { row ->
+            Row {
+                repeat(columns) { column ->
+                    val requester = requesters[row * columns + column]
+                    IconButton(
+                        onClick = {
+                            coroutineScope.launch {
+                                requester.bringIntoView()
+                            }
+                        }
+                    ) {
+                        val index = row * columns + column
+                        TextCircle(
+                            index.toString(),
+                            Modifier.size(50.dp)
+                        )
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Composable
+private fun TextCircle(text: String, modifier: Modifier = Modifier) {
+    Box(
+        modifier
+            .aspectRatio(1f)
+            .background(Color.Red, shape = CircleShape)
+    ) {
+        Text(
+            text = text,
+            color = Color.White,
+            modifier = Modifier.align(Alignment.Center)
+        )
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/BringRectangleIntoViewDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/BringRectangleIntoViewDemo.kt
deleted file mode 100644
index 423969e..0000000
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/BringRectangleIntoViewDemo.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.demos.relocation
-
-import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.border
-import androidx.compose.foundation.horizontalScroll
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.relocation.BringIntoViewRequester
-import androidx.compose.foundation.relocation.bringIntoViewRequester
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.material.Button
-import androidx.compose.material.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Color.Companion.Red
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.unit.dp
-import kotlinx.coroutines.launch
-
-@OptIn(ExperimentalFoundationApi::class)
-@Composable
-fun BringRectangleIntoViewDemo() {
-    with(LocalDensity.current) {
-        val bringIntoViewRequester = remember { BringIntoViewRequester() }
-        val coroutineScope = rememberCoroutineScope()
-        Column {
-            Text(
-                "This is a scrollable Box. Drag to scroll the Circle into view or click the " +
-                    "button to bring the circle into view."
-            )
-            Box(
-                Modifier
-                    .border(2.dp, Color.Black)
-                    .size(500f.toDp())
-                    .horizontalScroll(rememberScrollState())
-            ) {
-                Canvas(
-                    Modifier
-                        .size(1500f.toDp(), 500f.toDp())
-                        .bringIntoViewRequester(bringIntoViewRequester)
-                ) {
-                    drawCircle(color = Red, radius = 250f, center = Offset(750f, 250f))
-                }
-            }
-            Button(
-                onClick = {
-                    val circleCoordinates = Rect(500f, 0f, 1000f, 500f)
-                    coroutineScope.launch {
-                        bringIntoViewRequester.bringIntoView(circleCoordinates)
-                    }
-                }
-            ) {
-                Text("Bring circle into View")
-            }
-        }
-    }
-}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/BringRectangleIntoViewSampleWrappers.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/BringRectangleIntoViewSampleWrappers.kt
new file mode 100644
index 0000000..d825bd4
--- /dev/null
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/BringRectangleIntoViewSampleWrappers.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.demos.relocation
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.samples.BringIntoViewResponderSample
+import androidx.compose.foundation.samples.BringPartOfComposableIntoViewSample
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+
+@Composable
+fun BringRectangleIntoViewDemo() {
+    Column {
+        Text(
+            "This is a scrollable Box. Drag to scroll the Circle into view or click the " +
+                "button to bring the circle into view."
+        )
+        BringPartOfComposableIntoViewSample()
+    }
+}
+
+@Composable
+fun BringIntoViewResponderDemo() {
+    Column {
+        Text(
+            "Each cell in this box is focusable, use the arrow keys/tab/dpad to move focus " +
+                "around. The container will always put the last-requested rectangle in the top-" +
+                "left of itself."
+        )
+        BringIntoViewResponderSample()
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/RequestRectangleOnScreeenDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/RequestRectangleOnScreenDemo.kt
similarity index 100%
rename from compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/RequestRectangleOnScreeenDemo.kt
rename to compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/relocation/RequestRectangleOnScreenDemo.kt
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BringIntoViewSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BringIntoViewSamples.kt
index 49f37aa..7416de3 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BringIntoViewSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BringIntoViewSamples.kt
@@ -20,27 +20,41 @@
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.border
+import androidx.compose.foundation.focusable
 import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsFocusedAsState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.relocation.BringIntoViewRequester
+import androidx.compose.foundation.relocation.BringIntoViewResponder
 import androidx.compose.foundation.relocation.bringIntoViewRequester
+import androidx.compose.foundation.relocation.bringIntoViewResponder
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.material.Button
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.focusTarget
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.round
 import kotlinx.coroutines.launch
 
 @OptIn(ExperimentalFoundationApi::class)
@@ -110,3 +124,68 @@
         }
     }
 }
+
+@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
+@Sampled
+@Composable
+fun BringIntoViewResponderSample() {
+    var offset: IntOffset by remember { mutableStateOf(IntOffset.Zero) }
+    Box(
+        modifier = Modifier
+            .size(100.dp)
+            .layout { measurable, constraints ->
+                // Allow the content to be as big as it wants.
+                val placeable = measurable.measure(
+                    constraints.copy(
+                        maxWidth = Constraints.Infinity,
+                        maxHeight = Constraints.Infinity
+                    )
+                )
+
+                layout(constraints.maxWidth, constraints.maxHeight) {
+                    // Place the last-requested rectangle at the top-left of the box.
+                    placeable.place(offset)
+                }
+            }
+            .bringIntoViewResponder(remember {
+                object : BringIntoViewResponder {
+                    override fun calculateRectForParent(localRect: Rect): Rect {
+                        // Ask our parent to bring our top-left corner into view, since that's where
+                        // we're always going to position the requested content.
+                        return Rect(Offset.Zero, localRect.size)
+                    }
+
+                    override suspend fun bringChildIntoView(localRect: Rect) {
+                        // Offset the content right and down by the offset of the requested area so
+                        // that it will always be aligned to the top-left of the box.
+                        offset = -localRect.topLeft.round()
+                    }
+                }
+            })
+    ) {
+        LargeContentWithFocusableChildren()
+    }
+}
+
+@Composable
+private fun LargeContentWithFocusableChildren() {
+    Column {
+        repeat(10) { row ->
+            Row {
+                repeat(10) { column ->
+                    val interactionSource = remember { MutableInteractionSource() }
+                    val isFocused by interactionSource.collectIsFocusedAsState()
+                    Text(
+                        "$row x $column",
+                        Modifier
+                            .focusable(interactionSource = interactionSource)
+                            .then(
+                                if (isFocused) Modifier.border(1.dp, Color.Blue) else Modifier
+                            )
+                            .padding(8.dp)
+                    )
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt
index c069d7e..7205812 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt
@@ -23,19 +23,25 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.foundation.lazy.LazyRow
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.material.Button
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.flow.collect
 
 @Sampled
 @Composable
@@ -116,3 +122,45 @@
         }
     }
 }
+
+@Sampled
+@Composable
+fun UsingListScrollPositionForSideEffectSample() {
+    val listState = rememberLazyListState()
+    LaunchedEffect(listState) {
+        snapshotFlow { listState.firstVisibleItemIndex }
+            .collect {
+                // use the new index
+            }
+    }
+}
+
+@Sampled
+@Composable
+fun UsingListScrollPositionInCompositionSample() {
+    val listState = rememberLazyListState()
+    val isAtTop by remember {
+        derivedStateOf {
+            listState.firstVisibleItemIndex == 0 && listState.firstVisibleItemScrollOffset == 0
+        }
+    }
+    if (!isAtTop) {
+        ScrollToTopButton(listState)
+    }
+}
+
+@Sampled
+@Composable
+fun UsingListLayoutInfoForSideEffectSample() {
+    val listState = rememberLazyListState()
+    LaunchedEffect(listState) {
+        snapshotFlow { listState.layoutInfo.totalItemsCount }
+            .collect {
+                // use the new items count
+            }
+    }
+}
+
+@Composable
+private fun ScrollToTopButton(@Suppress("UNUSED_PARAMETER") listState: LazyListState) {
+}
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyGridSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyGridSamples.kt
index 139feee..4b3da5e 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyGridSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyGridSamples.kt
@@ -24,14 +24,22 @@
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.lazy.GridCells
 import androidx.compose.foundation.lazy.GridItemSpan
+import androidx.compose.foundation.lazy.LazyGridState
 import androidx.compose.foundation.lazy.LazyVerticalGrid
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.rememberLazyGridState
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.flow.collect
 
 @OptIn(ExperimentalFoundationApi::class)
 @Sampled
@@ -91,3 +99,49 @@
         }
     }
 }
+
+@OptIn(ExperimentalFoundationApi::class)
+@Sampled
+@Composable
+fun UsingGridScrollPositionForSideEffectSample() {
+    val gridState = rememberLazyGridState()
+    LaunchedEffect(gridState) {
+        snapshotFlow { gridState.firstVisibleItemIndex }
+            .collect {
+                // use the new index
+            }
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Sampled
+@Composable
+fun UsingGridScrollPositionInCompositionSample() {
+    val gridState = rememberLazyGridState()
+    val isAtTop by remember {
+        derivedStateOf {
+            gridState.firstVisibleItemIndex == 0 && gridState.firstVisibleItemScrollOffset == 0
+        }
+    }
+    if (!isAtTop) {
+        ScrollToTopButton(gridState)
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Sampled
+@Composable
+fun UsingGridLayoutInfoForSideEffectSample() {
+    val gridState = rememberLazyGridState()
+    LaunchedEffect(gridState) {
+        snapshotFlow { gridState.layoutInfo.totalItemsCount }
+            .collect {
+                // use the new items count
+            }
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun ScrollToTopButton(@Suppress("UNUSED_PARAMETER") gridState: LazyGridState) {
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridTest.kt
index 17e6f3b..4eab756 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridTest.kt
@@ -73,6 +73,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
+import com.google.common.collect.Range
+import com.google.common.truth.IntegerSubject
 import com.google.common.truth.Truth
 import kotlinx.coroutines.runBlocking
 import org.junit.Rule
@@ -985,3 +987,7 @@
             .assertPixels { Color.Green }
     }
 }
+
+internal fun IntegerSubject.isEqualTo(expected: Int, tolerance: Int) {
+    isIn(Range.closed(expected - tolerance, expected + tolerance))
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridsContentPaddingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridsContentPaddingTest.kt
index 6e61140..2ac7d84 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridsContentPaddingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridsContentPaddingTest.kt
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.AutoTestFrameClock
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.animateScrollBy
+import androidx.compose.foundation.gestures.scrollBy
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Spacer
@@ -67,12 +68,14 @@
 
     private var itemSize: Dp = Dp.Infinity
     private var smallPaddingSize: Dp = Dp.Infinity
+    private var itemSizePx = 50f
+    private var smallPaddingSizePx = 12f
 
     @Before
     fun before() {
         with(rule.density) {
-            itemSize = 50.toDp()
-            smallPaddingSize = 12.toDp()
+            itemSize = itemSizePx.toDp()
+            smallPaddingSize = smallPaddingSizePx.toDp()
         }
     }
 
@@ -416,6 +419,52 @@
     }
 
     @Test
+    fun column_overscrollWithContentPadding() {
+        lateinit var state: LazyGridState
+        rule.setContent {
+            state = rememberLazyGridState()
+            Box(modifier = Modifier.testTag(ContainerTag).size(itemSize + smallPaddingSize * 2)) {
+                LazyVerticalGrid(
+                    GridCells.Fixed(1),
+                    state = state,
+                    contentPadding = PaddingValues(
+                        vertical = smallPaddingSize
+                    )
+                ) {
+                    items(2) {
+                        Box(Modifier.testTag("$it").height(itemSize))
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertTopPositionInRootIsEqualTo(smallPaddingSize)
+            .assertHeightIsEqualTo(itemSize)
+
+        rule.onNodeWithTag("1")
+            .assertTopPositionInRootIsEqualTo(smallPaddingSize + itemSize)
+            .assertHeightIsEqualTo(itemSize)
+
+        rule.runOnIdle {
+            runBlocking {
+                // itemSizePx is the maximum offset, plus if we overscroll the content padding
+                // the layout mechanism will decide the item 0 is not needed until we start
+                // filling the over scrolled gap.
+                state.scrollBy(value = itemSizePx + smallPaddingSizePx * 1.5f)
+            }
+        }
+
+        rule.onNodeWithTag("1")
+            .assertTopPositionInRootIsEqualTo(smallPaddingSize)
+            .assertHeightIsEqualTo(itemSize)
+
+        rule.onNodeWithTag("0")
+            .assertTopPositionInRootIsEqualTo(smallPaddingSize - itemSize)
+            .assertHeightIsEqualTo(itemSize)
+    }
+
+    @Test
     fun totalPaddingLargerParentSize_initialState() {
         lateinit var state: LazyGridState
         rule.setContent {
@@ -1089,6 +1138,51 @@
     //     rule.onNodeWithTag("0").assertIsNotDisplayed()
     // }
 
+    // @Test
+    // fun row_overscrollWithContentPadding() {
+    //     lateinit var state: LazyListState
+    //     rule.setContent {
+    //         state = rememberLazyListState()
+    //         Box(modifier = Modifier.testTag(ContainerTag).size(itemSize + smallPaddingSize * 2)) {
+    //             LazyRow(
+    //                 state = state,
+    //                 contentPadding = PaddingValues(
+    //                     horizontal = smallPaddingSize
+    //                 )
+    //             ) {
+    //                 items(2) {
+    //                     Box(Modifier.testTag("$it").fillParentMaxSize())
+    //                 }
+    //             }
+    //         }
+    //     }
+
+    //     rule.onNodeWithTag("0")
+    //         .assertLeftPositionInRootIsEqualTo(smallPaddingSize)
+    //         .assertWidthIsEqualTo(itemSize)
+
+    //     rule.onNodeWithTag("1")
+    //         .assertLeftPositionInRootIsEqualTo(smallPaddingSize + itemSize)
+    //         .assertWidthIsEqualTo(itemSize)
+
+    //     rule.runOnIdle {
+    //         runBlocking {
+    //             // itemSizePx is the maximum offset, plus if we overscroll the content padding
+    //             // the layout mechanism will decide the item 0 is not needed until we start
+    //             // filling the over scrolled gap.
+    //             state.scrollBy(value = itemSizePx + smallPaddingSizePx * 1.5f)
+    //         }
+    //     }
+
+    //     rule.onNodeWithTag("1")
+    //         .assertLeftPositionInRootIsEqualTo(smallPaddingSize)
+    //         .assertWidthIsEqualTo(itemSize)
+
+    //     rule.onNodeWithTag("0")
+    //         .assertLeftPositionInRootIsEqualTo(smallPaddingSize - itemSize)
+    //         .assertWidthIsEqualTo(itemSize)
+    // }
+
     private fun LazyGridState.scrollBy(offset: Dp) {
         runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
             animateScrollBy(with(rule.density) { offset.roundToPx().toFloat() }, snap())
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt
index 6a697ff..3e855ad 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.lazy.grid
 
+import androidx.compose.animation.core.FloatSpringSpec
 import androidx.compose.foundation.AutoTestFrameClock
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.animateScrollBy
@@ -27,19 +28,23 @@
 import androidx.compose.foundation.lazy.GridCells
 import androidx.compose.foundation.lazy.LazyGridState
 import androidx.compose.foundation.lazy.LazyVerticalGrid
-import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.rememberLazyGridState
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.Dp
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import java.util.concurrent.TimeUnit
+import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
-import kotlin.math.roundToInt
 
 @MediumTest
 // @RunWith(Parameterized::class)
@@ -51,26 +56,37 @@
     private val vertical: Boolean
         get() = true // orientation == Orientation.Vertical
 
-    private val items = (1..20).toList()
+    private val itemsCount = 40
     private lateinit var state: LazyGridState
 
+    private val itemSizePx = 100
+    private var itemSizeDp = Dp.Unspecified
+    private var containerSizeDp = Dp.Unspecified
+
+    lateinit var scope: CoroutineScope
+
     @Before
     fun setup() {
+        with(rule.density) {
+            itemSizeDp = itemSizePx.toDp()
+            containerSizeDp = itemSizeDp * 3
+        }
         rule.setContent {
             state = rememberLazyGridState()
+            scope = rememberCoroutineScope()
             TestContent()
         }
     }
 
     @Test
-    fun testSetupWorks() {
+    fun setupWorks() {
         assertThat(state.firstVisibleItemIndex).isEqualTo(0)
         assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
         assertThat(state.firstVisibleItemIndex).isEqualTo(0)
     }
 
     @Test
-    fun snapToItemTest() = runBlocking {
+    fun scrollToItem() = runBlocking {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.scrollToItem(2)
         }
@@ -85,14 +101,57 @@
     }
 
     @Test
-    fun smoothScrollByTest() = runBlocking {
-        fun Int.dpToPx(): Int = with(rule.density) { dp.toPx().roundToInt() }
-        val scrollDistance = 320.dpToPx()
-        val itemSize = 101.dpToPx()
+    fun scrollToItemWithOffset() = runBlocking {
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.scrollToItem(6, 10)
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(6)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+    }
 
-        val expectedLine = scrollDistance / itemSize // resolves to 3
+    @Test
+    fun scrollToItemWithNegativeOffset() = runBlocking {
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.scrollToItem(6, -10)
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(4)
+        val item6Offset = state.layoutInfo.visibleItemsInfo.first { it.index == 6 }.offset.y
+        assertThat(item6Offset).isEqualTo(10)
+    }
+
+    @Test
+    fun scrollToItemWithPositiveOffsetLargerThanAvailableSize() = runBlocking {
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.scrollToItem(itemsCount - 6, 10)
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(itemsCount - 6)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0) // not 10
+    }
+
+    @Test
+    fun scrollToItemWithNegativeOffsetLargerThanAvailableSize() = runBlocking {
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.scrollToItem(1, -(itemSizePx + 10))
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0) // not -10
+    }
+
+    @Test
+    fun scrollToItemWithIndexLargerThanItemsCount() = runBlocking {
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.scrollToItem(itemsCount + 4)
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(itemsCount - 6)
+    }
+
+    @Test
+    fun animateScrollBy() = runBlocking {
+        val scrollDistance = 320
+
+        val expectedLine = scrollDistance / itemSizePx // resolves to 3
         val expectedItem = expectedLine * 2 // resolves to 6
-        val expectedOffset = scrollDistance % itemSize // resolves to ~17.dp.toIntPx()
+        val expectedOffset = scrollDistance % itemSizePx // resolves to 20px
 
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollBy(scrollDistance.toFloat())
@@ -102,25 +161,155 @@
     }
 
     @Test
-    fun smoothScrollToItemTest() = runBlocking {
+    fun animateScrollToItem() = runBlocking {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollToItem(10, 10)
         }
         assertThat(state.firstVisibleItemIndex).isEqualTo(10)
         assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+    }
+
+    @Test
+    fun animateScrollToItemWithOffset() = runBlocking {
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
-            state.animateScrollToItem(0, 10)
-            state.animateScrollToItem(11, 10)
+            state.animateScrollToItem(6, 10)
         }
-        // assertThat(state.firstVisibleItemIndex).isEqualTo(10)
-        // assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+        assertThat(state.firstVisibleItemIndex).isEqualTo(6)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+    }
+
+    @Test
+    fun animateScrollToItemWithNegativeOffset() = runBlocking {
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.animateScrollToItem(6, -10)
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(4)
+        val item6Offset = state.layoutInfo.visibleItemsInfo.first { it.index == 6 }.offset.y
+        assertThat(item6Offset).isEqualTo(10)
+    }
+
+    @Test
+    fun animateScrollToItemWithPositiveOffsetLargerThanAvailableSize() = runBlocking {
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.animateScrollToItem(itemsCount - 6, 10)
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(itemsCount - 6)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0) // not 10
+    }
+
+    @Test
+    fun animateScrollToItemWithNegativeOffsetLargerThanAvailableSize() = runBlocking {
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.animateScrollToItem(2, -(itemSizePx + 10))
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0) // not -10
+    }
+
+    @Test
+    fun animateScrollToItemWithIndexLargerThanItemsCount() = runBlocking {
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.animateScrollToItem(itemsCount + 2)
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(itemsCount - 6)
+    }
+
+    @Test
+    fun animatePerFrameForwardToVisibleItem() {
+        assertSpringAnimation(toIndex = 4)
+    }
+
+    @Test
+    fun animatePerFrameForwardToVisibleItemWithOffset() {
+        assertSpringAnimation(toIndex = 4, toOffset = 35)
+    }
+
+    @Test
+    fun animatePerFrameForwardToNotVisibleItem() {
+        assertSpringAnimation(toIndex = 16)
+    }
+
+    @Test
+    fun animatePerFrameForwardToNotVisibleItemWithOffset() {
+        assertSpringAnimation(toIndex = 20, toOffset = 35)
+    }
+
+    @Test
+    fun animatePerFrameBackward() {
+        assertSpringAnimation(toIndex = 2, fromIndex = 12)
+    }
+
+    @Test
+    fun animatePerFrameBackwardWithOffset() {
+        assertSpringAnimation(toIndex = 2, fromIndex = 10, fromOffset = 58)
+    }
+
+    @Test
+    fun animatePerFrameBackwardWithInitialOffset() {
+        assertSpringAnimation(toIndex = 0, toOffset = 40, fromIndex = 8)
+    }
+
+    private fun assertSpringAnimation(
+        toIndex: Int,
+        toOffset: Int = 0,
+        fromIndex: Int = 0,
+        fromOffset: Int = 0
+    ) {
+        if (fromIndex != 0 || fromOffset != 0) {
+            rule.runOnIdle {
+                runBlocking {
+                    state.scrollToItem(fromIndex, fromOffset)
+                }
+            }
+        }
+        rule.waitForIdle()
+
+        assertThat(state.firstVisibleItemIndex).isEqualTo(fromIndex)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(fromOffset)
+
+        rule.mainClock.autoAdvance = false
+
+        scope.launch {
+            state.animateScrollToItem(toIndex, toOffset)
+        }
+
+        while (!state.isScrollInProgress) {
+            Thread.sleep(5)
+        }
+
+        val startOffset = (fromIndex / 2 * itemSizePx + fromOffset).toFloat()
+        val endOffset = (toIndex / 2 * itemSizePx + toOffset).toFloat()
+        val spec = FloatSpringSpec()
+
+        val duration =
+            TimeUnit.NANOSECONDS.toMillis(spec.getDurationNanos(startOffset, endOffset, 0f))
+        rule.mainClock.advanceTimeByFrame()
+        var expectedTime = rule.mainClock.currentTime
+        for (i in 0..duration step FrameDuration) {
+            val nanosTime = TimeUnit.MILLISECONDS.toNanos(i)
+            val expectedValue =
+                spec.getValueFromNanos(nanosTime, startOffset, endOffset, 0f)
+            val actualValue =
+                (state.firstVisibleItemIndex / 2 * itemSizePx + state.firstVisibleItemScrollOffset)
+            assertWithMessage(
+                "On animation frame at $i index=${state.firstVisibleItemIndex} " +
+                    "offset=${state.firstVisibleItemScrollOffset} expectedValue=$expectedValue"
+            ).that(actualValue).isEqualTo(expectedValue.roundToInt(), tolerance = 1)
+
+            rule.mainClock.advanceTimeBy(FrameDuration)
+            expectedTime += FrameDuration
+            assertThat(expectedTime).isEqualTo(rule.mainClock.currentTime)
+            rule.waitForIdle()
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(toIndex)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(toOffset)
     }
 
     @Composable
     private fun TestContent() {
         if (vertical) {
-            LazyVerticalGrid(GridCells.Fixed(2), Modifier.height(300.dp), state) {
-                items(items) {
+            LazyVerticalGrid(GridCells.Fixed(2), Modifier.height(containerSizeDp), state) {
+                items(itemsCount) {
                     ItemContent()
                 }
             }
@@ -136,9 +325,9 @@
     @Composable
     private fun ItemContent() {
         val modifier = if (vertical) {
-            Modifier.height(101.dp)
+            Modifier.height(itemSizeDp)
         } else {
-            Modifier.width(101.dp)
+            Modifier.width(itemSizeDp)
         }
         Spacer(modifier)
     }
@@ -149,3 +338,5 @@
     //     fun params() = arrayOf(Orientation.Vertical, Orientation.Horizontal)
     // }
 }
+
+private val FrameDuration = 16L
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
index bf3cf61..766053b 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
@@ -32,7 +32,6 @@
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -142,11 +141,10 @@
 
     @Test
     fun animateScrollBy() = runBlocking {
-        fun Int.dpToPx(): Int = with(rule.density) { dp.toPx().roundToInt() }
-        val scrollDistance = 320.dpToPx()
+        val scrollDistance = 320
 
         val expectedIndex = scrollDistance / itemSizePx // resolves to 3
-        val expectedOffset = scrollDistance % itemSizePx // resolves to ~17.dp.toIntPx()
+        val expectedOffset = scrollDistance % itemSizePx // resolves to 20px
 
         withContext(Dispatchers.Main + AutoTestFrameClock()) {
             state.animateScrollBy(scrollDistance.toFloat())
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequesterViewIntegrationTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequesterViewIntegrationTest.kt
index becdc7b..5d682d7 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequesterViewIntegrationTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequesterViewIntegrationTest.kt
@@ -24,19 +24,12 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.relocation.BringIntoViewResponder.Companion.ModifierLocalBringIntoViewResponder
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.modifier.modifierLocalProvider
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.unit.IntOffset
@@ -103,9 +96,9 @@
     @Test
     fun bringIntoView_callsViewRequestRectangleOnScreen_whenResponderPresent() {
         val requesterOffset = IntOffset(1, 2)
-        val scrollOffset = 3
+        val scrollOffset = Offset(3f, 4f)
         val rectangleToRequest = Rect(Offset(10f, 20f), Size(30f, 40f))
-        val expectedRectangle = AndroidRect(11, 38, 41, 78)
+        val expectedRectangle = AndroidRect(14, 26, 44, 66)
         lateinit var scope: CoroutineScope
         lateinit var parent: FakeScrollable
         val bringIntoViewRequester = BringIntoViewRequester()
@@ -119,7 +112,7 @@
                     Box(
                         Modifier
                             .size(10.dp)
-                            .verticalScroll(rememberScrollState(scrollOffset))
+                            .fakeScrollable(scrollOffset) {}
                     ) {
                         Box(
                             Modifier
@@ -148,7 +141,7 @@
         }
     }
 
-    @Ignore("b/216652644")
+    @Ignore("This use case can't be supported until BringIntoView is in ui: b/216652644")
     @Test
     fun bringIntoView_propagatesThroughIntermediateView() {
         val requesterOffset = IntOffset(1, 2)
@@ -160,7 +153,9 @@
         rule.setContent {
             scope = rememberCoroutineScope()
             AndroidView(
-                modifier = Modifier.testBringIntoViewResponder { requests += it },
+                modifier = Modifier
+                    // This offset needs to be non-zero or it won't see the request at all.
+                    .fakeScrollable { requests += it },
                 factory = { context ->
                     val parent = FakeScrollable(context)
                     val child = ComposeView(context)
@@ -189,26 +184,6 @@
         }
     }
 
-    private fun Modifier.testBringIntoViewResponder(onBringIntoView: (Rect) -> Unit): Modifier =
-        composed {
-            val screenRequester = remember { BringRectangleOnScreenRequester() }
-            val responder = remember {
-                object : BringIntoViewResponder {
-                    override suspend fun bringIntoView(rect: Rect) {
-                        onBringIntoView(rect)
-                    }
-
-                    override fun toLocalRect(
-                        rect: Rect,
-                        layoutCoordinates: LayoutCoordinates
-                    ): Rect = rect
-                }
-            }
-
-            bringRectangleOnScreenRequester(screenRequester)
-                .modifierLocalProvider(ModifierLocalBringIntoViewResponder) { responder }
-        }
-
     /** A view that records calls to [requestChildRectangleOnScreen] for testing. */
     private class FakeScrollable(context: Context) : FrameLayout(context) {
         val requests = mutableListOf<RectangleRequest>()
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponderTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponderTest.kt
index 59fd60c..814c13e 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponderTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponderTest.kt
@@ -17,37 +17,30 @@
 package androidx.compose.foundation.relocation
 
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.TestActivity
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.relocation.BringIntoViewResponder.Companion.ModifierLocalBringIntoViewResponder
-import androidx.compose.foundation.relocation.LayoutCoordinateSubject.Companion.assertThat
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.boundsInParent
-import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.layout.positionInParent
-import androidx.compose.ui.modifier.modifierLocalProvider
-import androidx.compose.foundation.TestActivity
-import androidx.compose.foundation.relocation.ScrollableResponder.Companion.LocalRect
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.google.common.truth.Fact.simpleFact
-import com.google.common.truth.FailureMetadata
-import com.google.common.truth.Subject
-import com.google.common.truth.Subject.Factory
-import com.google.common.truth.Truth
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
-import org.junit.Before
+import kotlinx.coroutines.test.TestCoroutineScope
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class BringIntoViewResponderTest {
@@ -55,28 +48,18 @@
     @get:Rule
     val rule = createAndroidComposeRule<TestActivity>()
 
-    lateinit var density: Density
+    fun Float.toDp(): Dp = with(rule.density) { [email protected]() }
 
-    @Before
-    fun setup() {
-        density = Density(rule.activity)
-    }
-
-    fun Float.toDp(): Dp = with(density) { [email protected]() }
-
-    @OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
     @Test
     fun zeroSizedItem_zeroSizedParent_bringIntoView() {
         // Arrange.
-        lateinit var layoutCoordinates: LayoutCoordinates
         val bringIntoViewRequester = BringIntoViewRequester()
-        val scrollableParent = ScrollableResponder()
+        lateinit var requestedRect: Rect
         rule.setContent {
             Box(
                 Modifier
-                    .modifierLocalProvider(ModifierLocalBringIntoViewResponder) { scrollableParent }
+                    .fakeScrollable { requestedRect = it }
                     .bringIntoViewRequester(bringIntoViewRequester)
-                    .onGloballyPositioned { layoutCoordinates = it }
             )
         }
 
@@ -84,27 +67,71 @@
         runBlocking { bringIntoViewRequester.bringIntoView() }
 
         // Assert.
-        assertThat(scrollableParent.callersLayoutCoordinates).isEqualTo(layoutCoordinates)
-        assertThat(scrollableParent.nonLocalRect).isEqualTo(Rect.Zero)
-        assertThat(scrollableParent.requestedRect).isEqualTo(LocalRect)
+        rule.runOnIdle {
+            assertThat(requestedRect).isEqualTo(Rect.Zero)
+        }
     }
 
-    @OptIn(ExperimentalComposeUiApi::class,
-        androidx.compose.foundation.ExperimentalFoundationApi::class
-    )
     @Test
-    fun bringIntoView_itemWithSize() {
+    fun bringIntoView_rectInChild() {
         // Arrange.
-        lateinit var layoutCoordinates: LayoutCoordinates
         val bringIntoViewRequester = BringIntoViewRequester()
-        val scrollableParent = ScrollableResponder()
+        lateinit var requestedRect: Rect
         rule.setContent {
             Box(
                 Modifier
-                    .size(20f.toDp(), 10f.toDp())
-                    .modifierLocalProvider(ModifierLocalBringIntoViewResponder) { scrollableParent }
+                    .fakeScrollable { requestedRect = it }
                     .bringIntoViewRequester(bringIntoViewRequester)
-                    .onGloballyPositioned { layoutCoordinates = it }
+            )
+        }
+
+        // Act.
+        runBlocking { bringIntoViewRequester.bringIntoView(Rect(1f, 2f, 3f, 4f)) }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(requestedRect).isEqualTo(Rect(1f, 2f, 3f, 4f))
+        }
+    }
+
+    @Test
+    fun bringIntoView_childWithSize() {
+        // Arrange.
+        val bringIntoViewRequester = BringIntoViewRequester()
+        lateinit var requestedRect: Rect
+        rule.setContent {
+            Box(Modifier) {
+                Box(
+                    Modifier
+                        .fakeScrollable { requestedRect = it }
+                        .size(20f.toDp(), 10f.toDp())
+                        .offset { IntOffset(40, 30) }
+                        .bringIntoViewRequester(bringIntoViewRequester)
+                )
+            }
+        }
+
+        // Act.
+        runBlocking { bringIntoViewRequester.bringIntoView() }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(requestedRect).isEqualTo(Rect(40f, 30f, 60f, 40f))
+        }
+    }
+
+    @Test
+    fun bringIntoView_childBiggerThanParent() {
+        // Arrange.
+        val bringIntoViewRequester = BringIntoViewRequester()
+        lateinit var requestedRect: Rect
+        rule.setContent {
+            Box(
+                Modifier
+                    .size(1f.toDp())
+                    .fakeScrollable { requestedRect = it }
+                    .size(20f.toDp(), 10f.toDp())
+                    .bringIntoViewRequester(bringIntoViewRequester)
             )
         }
 
@@ -112,68 +139,192 @@
         runBlocking { bringIntoViewRequester.bringIntoView() }
 
         // Assert.
+        rule.runOnIdle {
+            assertThat(requestedRect).isEqualTo(Rect(0f, 0f, 20f, 10f))
+        }
+    }
+
+    @Test
+    fun bringIntoView_propagatesToMultipleResponders() {
+        // Arrange.
+        lateinit var outerRequest: Rect
+        lateinit var innerRequest: Rect
+        val bringIntoViewRequester = BringIntoViewRequester()
+        rule.setContent {
+            Box(
+                Modifier
+                    .fakeScrollable { outerRequest = it }
+                    .offset(2f.toDp(), 1f.toDp())
+                    .fakeScrollable { innerRequest = it }
+                    .size(20f.toDp(), 10f.toDp())
+                    .bringIntoViewRequester(bringIntoViewRequester)
+            )
+        }
+
+        // Act.
+        runBlocking { bringIntoViewRequester.bringIntoView() }
+
         // Assert.
-        assertThat(scrollableParent.callersLayoutCoordinates).isEqualTo(layoutCoordinates)
-        assertThat(scrollableParent.nonLocalRect).isEqualTo(Rect(0f, 0f, 20f, 10f))
-        assertThat(scrollableParent.requestedRect).isEqualTo(LocalRect)
+        rule.runOnIdle {
+            assertThat(innerRequest).isEqualTo(Rect(0f, 0f, 20f, 10f))
+            assertThat(outerRequest).isEqualTo(Rect(2f, 1f, 22f, 11f))
+        }
     }
-}
 
-private class LayoutCoordinateSubject(metadata: FailureMetadata?, val actual: LayoutCoordinates?) :
-    Subject(metadata, actual) {
-
-    fun isEqualTo(expected: LayoutCoordinates?) {
-        if (expected == null) {
-            assertThat(actual).isNull()
-            return
-        }
-        if (actual == null) {
-            failWithActual(simpleFact("Expected non-null layout coordinates."))
-            return
-        }
-        if (expected.size != actual.size) {
-            failWithoutActual(
-                simpleFact("Expected size be ${expected.size}"),
-                simpleFact("But was ${actual.size}")
+    @Test
+    fun bringIntoView_onlyPropagatesUp() {
+        // Arrange.
+        lateinit var parentRequest: Rect
+        var childRequest: Rect? = null
+        val bringIntoViewRequester = BringIntoViewRequester()
+        rule.setContent {
+            Box(
+                Modifier
+                    .fakeScrollable { parentRequest = it }
+                    .bringIntoViewRequester(bringIntoViewRequester)
+                    .fakeScrollable { childRequest = it }
             )
         }
-        if (expected.positionInParent() != actual.positionInParent()) {
-            failWithoutActual(
-                simpleFact("Expected bounds in parent to be ${expected.boundsInParent()}"),
-                simpleFact("But was ${actual.boundsInParent()}")
+
+        // Act.
+        runBlocking { bringIntoViewRequester.bringIntoView() }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(parentRequest).isEqualTo(Rect.Zero)
+            assertThat(childRequest).isNull()
+        }
+    }
+
+    @Test
+    fun bringIntoView_propagatesUp_whenRectForParentReturnsInput() {
+        // Arrange.
+        lateinit var parentRequest: Rect
+        var childRequest: Rect? = null
+        val bringIntoViewRequester = BringIntoViewRequester()
+        rule.setContent {
+            Box(
+                Modifier
+                    .fakeScrollable { parentRequest = it }
+                    .fakeScrollable { childRequest = it }
+                    .bringIntoViewRequester(bringIntoViewRequester)
             )
         }
+
+        // Act.
+        runBlocking { bringIntoViewRequester.bringIntoView() }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(parentRequest).isEqualTo(Rect.Zero)
+            assertThat(childRequest).isEqualTo(Rect.Zero)
+        }
     }
 
-    companion object {
-        fun assertThat(layoutCoordinates: LayoutCoordinates?): LayoutCoordinateSubject {
-            return Truth.assertAbout(subjectFactory).that(layoutCoordinates)
+    @Test
+    fun bringIntoView_translatesByCalculateRectForParent() {
+        // Arrange.
+        lateinit var requestedRect: Rect
+        val bringIntoViewRequester = BringIntoViewRequester()
+        rule.setContent {
+            Box(
+                Modifier
+                    .fakeScrollable { requestedRect = it }
+                    .fakeScrollable(Offset(2f, 3f)) {}
+                    .bringIntoViewRequester(bringIntoViewRequester)
+            )
         }
 
-        private val subjectFactory =
-            Factory<LayoutCoordinateSubject, LayoutCoordinates?> { metadata, actual ->
-                LayoutCoordinateSubject(metadata, actual)
-            }
-    }
-}
+        // Act.
+        runBlocking { bringIntoViewRequester.bringIntoView() }
 
-@OptIn(ExperimentalFoundationApi::class)
-private class ScrollableResponder : BringIntoViewResponder {
-    lateinit var requestedRect: Rect
-    lateinit var nonLocalRect: Rect
-    lateinit var callersLayoutCoordinates: LayoutCoordinates
-
-    override suspend fun bringIntoView(rect: Rect) {
-        requestedRect = rect
+        // Assert.
+        rule.runOnIdle {
+            assertThat(requestedRect).isEqualTo(Rect(2f, 3f, 2f, 3f))
+        }
     }
 
-    override fun toLocalRect(rect: Rect, layoutCoordinates: LayoutCoordinates): Rect {
-        nonLocalRect = rect
-        callersLayoutCoordinates = layoutCoordinates
-        return LocalRect
+    @Test
+    fun bringChildIntoView_isCalled_whenRectForParentDoesNotReturnInput() {
+        // Arrange.
+        var requestedRect: Rect? = null
+        val bringIntoViewRequester = BringIntoViewRequester()
+        rule.setContent {
+            Box(
+                Modifier
+                    .fakeScrollable(Offset.Zero) { requestedRect = it }
+                    .bringIntoViewRequester(bringIntoViewRequester)
+            )
+        }
+
+        // Act.
+        runBlocking { bringIntoViewRequester.bringIntoView() }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(requestedRect).isEqualTo(Rect.Zero)
+        }
     }
 
-    companion object {
-        val LocalRect = Rect(10f, 10f, 10f, 10f)
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun bringChildIntoView_calledConcurrentlyOnAllResponders() {
+        // Arrange.
+        var childStarted = false
+        var parentStarted = false
+        var childFinished = false
+        var parentFinished = false
+        val bringIntoViewRequester = BringIntoViewRequester()
+        rule.setContent {
+            Box(
+                Modifier
+                    .fakeScrollable {
+                        parentStarted = true
+                        try {
+                            awaitCancellation()
+                        } finally {
+                            parentFinished = true
+                        }
+                    }
+                    .fakeScrollable {
+                        childStarted = true
+                        try {
+                            awaitCancellation()
+                        } finally {
+                            childFinished = true
+                        }
+                    }
+                    .bringIntoViewRequester(bringIntoViewRequester)
+            )
+        }
+        val testScope = TestCoroutineScope().apply {
+            pauseDispatcher()
+        }
+        val requestJob = testScope.launch {
+            bringIntoViewRequester.bringIntoView()
+        }
+        rule.waitForIdle()
+
+        assertThat(childStarted).isFalse()
+        assertThat(parentStarted).isFalse()
+        assertThat(childFinished).isFalse()
+        assertThat(parentFinished).isFalse()
+
+        // Act.
+        testScope.advanceUntilIdle()
+
+        // Assert.
+        assertThat(childStarted).isTrue()
+        assertThat(parentStarted).isTrue()
+        assertThat(childFinished).isFalse()
+        assertThat(parentFinished).isFalse()
+
+        // Act.
+        requestJob.cancel()
+        testScope.advanceUntilIdle()
+
+        // Assert.
+        assertThat(childFinished).isTrue()
+        assertThat(parentFinished).isTrue()
     }
-}
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/relocation/FakeScrollable.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/relocation/FakeScrollable.kt
new file mode 100644
index 0000000..131236c
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/relocation/FakeScrollable.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.relocation
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+
+/**
+ * Returns a [bringIntoViewResponder] modifier that implements [BringIntoViewResponder] by
+ * offsetting the local rect by [parentOffset] and handling the request by calling
+ * [onBringIntoView]. Note that [onBringIntoView] will not be called if [parentOffset] is zero,
+ * since that means the scrollable doesn't actually need to scroll anything to satisfy the
+ * request.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+internal fun Modifier.fakeScrollable(
+    parentOffset: Offset = Offset.Zero,
+    onBringIntoView: suspend (Rect) -> Unit
+): Modifier = bringIntoViewResponder(
+    object : BringIntoViewResponder {
+        override fun calculateRectForParent(localRect: Rect): Rect =
+            localRect.translate(parentOffset)
+
+        override suspend fun bringChildIntoView(localRect: Rect) {
+            onBringIntoView(localRect)
+        }
+    })
+    .wrapContentSize(align = Alignment.TopStart, unbounded = true)
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.android.kt
new file mode 100644
index 0000000..b0b12cb
--- /dev/null
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.android.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.relocation
+
+import android.graphics.Rect as AndroidRect
+import android.view.View
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.platform.LocalView
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+internal actual fun rememberDefaultBringIntoViewParent(): BringIntoViewParent {
+    val view = LocalView.current
+    return remember(view) { AndroidBringIntoViewParent(view) }
+}
+
+/**
+ * A [BringIntoViewParent] that delegates to the [View] hosting the composition.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+private class AndroidBringIntoViewParent(private val view: View) : BringIntoViewParent {
+    override suspend fun bringChildIntoView(rect: Rect, childCoordinates: LayoutCoordinates) {
+        val childOffset = childCoordinates.positionInRoot()
+        val rootRect = rect.translate(childOffset)
+        view.requestRectangleOnScreen(rootRect.toRect(), false)
+    }
+}
+
+private fun Rect.toRect() = AndroidRect(left.toInt(), top.toInt(), right.toInt(), bottom.toInt())
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/relocation/BringRectangleOnScreen.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/relocation/BringRectangleOnScreen.android.kt
deleted file mode 100644
index 88c516a..0000000
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/relocation/BringRectangleOnScreen.android.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.relocation
-
-import android.view.View
-import androidx.compose.ui.Modifier
-import android.graphics.Rect
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.ui.composed
-import androidx.compose.ui.platform.LocalView
-import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.ui.geometry.Rect as ComposeRect
-
-/**
- * Platform specific internal API to bring a rectangle into view.
- */
-internal actual class BringRectangleOnScreenRequester {
-    internal var view: View? = null
-    actual fun bringRectangleOnScreen(rect: ComposeRect) {
-        view?.requestRectangleOnScreen(rect.toRect(), false)
-    }
-}
-
-/**
- * Companion Modifier to [BringRectangleOnScreenRequester].
- */
-internal actual fun Modifier.bringRectangleOnScreenRequester(
-    bringRectangleOnScreenRequester: BringRectangleOnScreenRequester
-): Modifier = composed(
-    debugInspectorInfo {
-        name = "bringRectangleOnScreenRequester"
-        properties["bringRectangleOnScreenRequester"] = bringRectangleOnScreenRequester
-    }
-) {
-    val view = LocalView.current
-    DisposableEffect(view) {
-        bringRectangleOnScreenRequester.view = view
-        onDispose {
-            bringRectangleOnScreenRequester.view = null
-        }
-    }
-    Modifier
-}
-
-private fun ComposeRect.toRect(): Rect {
-    return Rect(left.toInt(), top.toInt(), right.toInt(), bottom.toInt())
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index 9334c0b..e50f84e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -26,10 +26,7 @@
 import androidx.compose.foundation.gestures.Orientation.Vertical
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.relocation.BringIntoViewResponder
-import androidx.compose.foundation.relocation.BringIntoViewResponder.Companion.ModifierLocalBringIntoViewResponder
-import androidx.compose.foundation.relocation.BringRectangleOnScreenRequester
-import androidx.compose.foundation.relocation.bringIntoView
-import androidx.compose.foundation.relocation.bringRectangleOnScreenRequester
+import androidx.compose.foundation.relocation.bringIntoViewResponder
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.mutableStateOf
@@ -51,11 +48,8 @@
 import androidx.compose.ui.input.pointer.PointerEventType
 import androidx.compose.ui.input.pointer.PointerType
 import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.OnGloballyPositionedModifier
-import androidx.compose.ui.modifier.ModifierLocalConsumer
+import androidx.compose.ui.layout.OnRemeasuredModifier
 import androidx.compose.ui.modifier.ModifierLocalProvider
-import androidx.compose.ui.modifier.ModifierLocalReadScope
 import androidx.compose.ui.modifier.modifierLocalOf
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.Density
@@ -65,7 +59,6 @@
 import androidx.compose.ui.util.fastAll
 import androidx.compose.ui.util.fastForEach
 import kotlin.math.abs
-import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 
 /**
@@ -108,6 +101,7 @@
     overScrollController = null
 )
 
+@OptIn(ExperimentalFoundationApi::class)
 internal fun Modifier.scrollable(
     state: ScrollableState,
     orientation: Orientation,
@@ -129,7 +123,7 @@
     },
     factory = {
         val overscrollModifier = overScrollController?.let { Modifier.overScroll(it) } ?: Modifier
-        val bringIntoViewModifier = remember(orientation, state, reverseDirection) {
+        val bringIntoViewResponder = remember(orientation, state, reverseDirection) {
             BringIntoViewResponder(orientation, state, reverseDirection)
         }
 
@@ -140,8 +134,7 @@
         }
 
         Modifier
-            .then(bringIntoViewModifier)
-            .bringRectangleOnScreenRequester(bringIntoViewModifier.bringRectangleOnScreenRequester)
+            .then(bringIntoViewResponder.modifier)
             .then(overscrollModifier)
             .pointerScrollable(
                 interactionSource,
@@ -468,89 +461,40 @@
     private val orientation: Orientation,
     private val scrollableState: ScrollableState,
     private val reverseDirection: Boolean,
-) : ModifierLocalConsumer,
-    ModifierLocalProvider<BringIntoViewResponder>,
-    BringIntoViewResponder,
-    OnGloballyPositionedModifier {
+) : BringIntoViewResponder, OnRemeasuredModifier {
+    private var size: IntSize = IntSize.Zero
 
-    private fun Float.reverseIfNeeded(): Float = if (reverseDirection) this * -1 else this
+    val modifier = Modifier.bringIntoViewResponder(this)
+        .then(this)
 
-    // Read the modifier local and save a reference to the parent.
-    private lateinit var parent: BringIntoViewResponder
-    override fun onModifierLocalsUpdated(scope: ModifierLocalReadScope) {
-        parent = scope.run { ModifierLocalBringIntoViewResponder.current }
+    override fun onRemeasured(size: IntSize) {
+        this.size = size
     }
 
-    val bringRectangleOnScreenRequester = BringRectangleOnScreenRequester()
-
-    // Populate the modifier local with this object.
-    override val key = ModifierLocalBringIntoViewResponder
-    override val value = this
-
-    // LayoutCoordinates of this item.
-    private lateinit var layoutCoordinates: LayoutCoordinates
-    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
-        layoutCoordinates = coordinates
-    }
-
-    override suspend fun bringIntoView(rect: Rect) {
-
-        val destRect = computeDestination(rect)
-
-        // For the item to be visible, if needs to be in the viewport of all its ancestors.
-        // Note: For now we run both of these in parallel, but in the future we could make this
-        // configurable. (The child relocation could be executed before the parent, or parent
-        // before the child).
-        coroutineScope {
-            // Bring the requested Child into this parent's view.
-            launch {
-                performBringIntoView(rect, destRect)
-            }
-
-            // If the parent is another BringIntoViewResponder, call bringIntoView.
-            launch {
-                parent.bringIntoView(
-                    parent.toLocalRect(destRect, [email protected]),
-                    bringRectangleOnScreenRequester
-                )
-            }
-        }
-    }
-
-    override fun toLocalRect(rect: Rect, layoutCoordinates: LayoutCoordinates): Rect {
-        // Translate the supplied layout coordinates into the coordinate system of this parent.
-        val parentBoundingBox = this.layoutCoordinates.localBoundingBoxOf(layoutCoordinates, false)
-
-        // Translate the rect to this parent's local coordinates.
-        return rect.translate(parentBoundingBox.topLeft)
-    }
-
-    /**
-     * Compute the destination given the source rectangle and current bounds.
-     *
-     * @param source The bounding box of the item that sent the request to be brought into view.
-     * @return the destination rectangle.
-     */
-    fun computeDestination(source: Rect): Rect {
-        val size = layoutCoordinates.size.toSize()
+    override fun calculateRectForParent(localRect: Rect): Rect {
+        val size = size.toSize()
         return when (orientation) {
-            Vertical ->
-                source.translate(0f, relocationDistance(source.top, source.bottom, size.height))
-            Horizontal ->
-                source.translate(relocationDistance(source.left, source.right, size.width), 0f)
+            Vertical -> localRect.translate(
+                translateX = 0f,
+                translateY = relocationDistance(localRect.top, localRect.bottom, size.height)
+            )
+            Horizontal -> localRect.translate(
+                translateX = relocationDistance(localRect.left, localRect.right, size.width),
+                translateY = 0f
+            )
         }
     }
 
-    /**
-     * Using the source and destination bounds, perform an animated scroll.
-     */
-    suspend fun performBringIntoView(source: Rect, destination: Rect) {
+    override suspend fun bringChildIntoView(localRect: Rect) {
+        val destRect = calculateRectForParent(localRect)
         val offset = when (orientation) {
-            Vertical -> source.top - destination.top
-            Horizontal -> source.left - destination.left
+            Vertical -> localRect.top - destRect.top
+            Horizontal -> localRect.left - destRect.left
         }
         scrollableState.animateScrollBy(offset.reverseIfNeeded())
     }
+
+    private fun Float.reverseIfNeeded(): Float = if (reverseDirection) this * -1 else this
 }
 
 // Calculate the offset needed to bring one of the edges into view. The leadingEdge is the side
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGridState.kt
index e463d58..7c1f959 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGridState.kt
@@ -92,7 +92,18 @@
         LazyGridScrollPosition(firstVisibleItemIndex, firstVisibleItemScrollOffset)
 
     /**
-     * The index of the first item that is visible
+     * The index of the first item that is visible.
+     *
+     * Note that this property is observable and if you use it in the composable function it will
+     * be recomposed on every change causing potential performance issues.
+     *
+     * If you want to run some side effects like sending an analytics event or updating a state
+     * based on this value consider using "snapshotFlow":
+     * @sample androidx.compose.foundation.samples.UsingGridScrollPositionForSideEffectSample
+     *
+     * If you need to use it in the composition then consider wrapping the calculation into a
+     * derived state in order to only have recompositions when the derived value changes:
+     * @sample androidx.compose.foundation.samples.UsingGridScrollPositionInCompositionSample
      */
     val firstVisibleItemIndex: Int get() = scrollPosition.observableIndex
 
@@ -108,6 +119,15 @@
     /**
      * The object of [LazyGridLayoutInfo] calculated during the last layout pass. For example,
      * you can use it to calculate what items are currently visible.
+     *
+     * Note that this property is observable and is updated after every scroll or remeasure.
+     * If you use it in the composable function it will be recomposed on every change causing
+     * potential performance issues including infinity recomposition loop.
+     * Therefore, avoid using it in the composition.
+     *
+     * If you want to run some side effects like sending an analytics event or updating a state
+     * based on this value consider using "snapshotFlow":
+     * @sample androidx.compose.foundation.samples.UsingGridLayoutInfoForSideEffectSample
      */
     val layoutInfo: LazyGridLayoutInfo get() = layoutInfoState.value
 
@@ -145,6 +165,11 @@
     /**
      * Needed for [animateScrollToItem]. Updated on every measure.
      */
+    internal var slotsPerLine: Int = 0
+
+    /**
+     * Needed for [animateScrollToItem]. Updated on every measure.
+     */
     internal var density: Density = Density(1f, 1f)
 
     /**
@@ -204,17 +229,14 @@
      * Instantly brings the item at [index] to the top of the viewport, offset by [scrollOffset]
      * pixels.
      *
-     * Cancels the currently running scroll, if any, and suspends until the cancellation is
-     * complete.
-     *
-     * @param index the data index to snap to. Must be between 0 and the number of elements.
-     * @param scrollOffset the number of pixels past the start of the item to snap to. Must
-     * not be negative.
+     * @param index the index to which to scroll. Must be non-negative.
+     * @param scrollOffset the offset that the item should end up after the scroll. Note that
+     * positive offset refers to forward scroll, so in a top-to-bottom list, positive offset will
+     * scroll the item further upward (taking it partly offscreen).
      */
     suspend fun scrollToItem(
         /*@IntRange(from = 0)*/
         index: Int,
-        /*@IntRange(from = 0)*/
         scrollOffset: Int = 0
     ) {
         return scrollableState.scroll {
@@ -235,9 +257,6 @@
      * performed within a [scroll] block (even if they don't call any other methods on this
      * object) in order to guarantee that mutual exclusion is enforced.
      *
-     * Cancels the currently running scroll, if any, and suspends until the cancellation is
-     * complete.
-     *
      * If [scroll] is called from elsewhere, this will be canceled.
      */
     override suspend fun scroll(
@@ -332,19 +351,17 @@
     /**
      * Animate (smooth scroll) to the given item.
      *
-     * @param index the index to which to scroll
-     * @param scrollOffset the offset that the item should end up after the scroll (same as
-     * [scrollToItem]) - note that positive offset refers to forward scroll, so in a
-     * top-to-bottom grid, positive offset will scroll the item further upward (taking it partly
-     * offscreen)
+     * @param index the index to which to scroll. Must be non-negative.
+     * @param scrollOffset the offset that the item should end up after the scroll. Note that
+     * positive offset refers to forward scroll, so in a top-to-bottom list, positive offset will
+     * scroll the item further upward (taking it partly offscreen).
      */
     suspend fun animateScrollToItem(
         /*@IntRange(from = 0)*/
         index: Int,
-        /*@IntRange(from = 0)*/
         scrollOffset: Int = 0
     ) {
-        doSmoothScrollToItem(index, scrollOffset)
+        doSmoothScrollToItem(index, scrollOffset, slotsPerLine)
     }
 
     /**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
index 7581a4f..87a7ac2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
@@ -88,13 +88,28 @@
         LazyListScrollPosition(firstVisibleItemIndex, firstVisibleItemScrollOffset)
 
     /**
-     * The index of the first item that is visible
+     * The index of the first item that is visible.
+     *
+     * Note that this property is observable and if you use it in the composable function it will
+     * be recomposed on every change causing potential performance issues.
+     *
+     * If you want to run some side effects like sending an analytics event or updating a state
+     * based on this value consider using "snapshotFlow":
+     * @sample androidx.compose.foundation.samples.UsingListScrollPositionForSideEffectSample
+     *
+     * If you need to use it in the composition then consider wrapping the calculation into a
+     * derived state in order to only have recompositions when the derived value changes:
+     * @sample androidx.compose.foundation.samples.UsingListScrollPositionInCompositionSample
      */
     val firstVisibleItemIndex: Int get() = scrollPosition.observableIndex
 
     /**
      * The scroll offset of the first visible item. Scrolling forward is positive - i.e., the
-     * amount that the item is offset backwards
+     * amount that the item is offset backwards.
+     *
+     * Note that this property is observable and if you use it in the composable function it will
+     * be recomposed on every scroll causing potential performance issues.
+     * @see firstVisibleItemIndex for samples with the recommended usage patterns.
      */
     val firstVisibleItemScrollOffset: Int get() = scrollPosition.observableScrollOffset
 
@@ -104,6 +119,15 @@
     /**
      * The object of [LazyListLayoutInfo] calculated during the last layout pass. For example,
      * you can use it to calculate what items are currently visible.
+     *
+     * Note that this property is observable and is updated after every scroll or remeasure.
+     * If you use it in the composable function it will be recomposed on every change causing
+     * potential performance issues including infinity recomposition loop.
+     * Therefore, avoid using it in the composition.
+     *
+     * If you want to run some side effects like sending an analytics event or updating a state
+     * based on this value consider using "snapshotFlow":
+     * @sample androidx.compose.foundation.samples.UsingListLayoutInfoForSideEffectSample
      */
     val layoutInfo: LazyListLayoutInfo get() = layoutInfoState.value
 
@@ -321,7 +345,7 @@
      * @param index the index to which to scroll. Must be non-negative.
      * @param scrollOffset the offset that the item should end up after the scroll. Note that
      * positive offset refers to forward scroll, so in a top-to-bottom list, positive offset will
-     * scroll the item further upward (taking it partly offscreen)
+     * scroll the item further upward (taking it partly offscreen).
      */
     suspend fun animateScrollToItem(
         /*@IntRange(from = 0)*/
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
index 7cd6054..3d63d6d4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
@@ -221,14 +221,15 @@
         val itemsProvider = stateOfItemsProvider.value
         state.updateScrollPositionIfTheFirstItemWasMoved(itemsProvider)
 
-        // Update the state's cached Density
-        state.density = this
-
         val spanLayoutProvider = stateOfSpanLayoutProvider.value
         // Resolve slotsPerLine.
         val resolvedSlotsPerLine = slotsPerLine(constraints)
         spanLayoutProvider.slotsPerLine = resolvedSlotsPerLine
 
+        // Update the state's cached Density and slotsPerLine
+        state.density = this
+        state.slotsPerLine = resolvedSlotsPerLine
+
         val spaceBetweenLinesDp = if (isVertical) {
             requireNotNull(verticalArrangement).spacing
         } else {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
index 3e7e34b..5ed721c 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
@@ -161,7 +161,9 @@
             val toScrollBack = maxOffset - currentMainAxisOffset
             currentFirstLineScrollOffset -= toScrollBack
             currentMainAxisOffset += toScrollBack
-            while (currentFirstLineScrollOffset < 0 && currentFirstLineIndex > LineIndex(0)) {
+            while (currentFirstLineScrollOffset < beforeContentPadding &&
+                currentFirstLineIndex > LineIndex(0)
+            ) {
                 val previousIndex = LineIndex(currentFirstLineIndex.value - 1)
                 val measuredLine = measuredLineProvider.getAndMeasure(previousIndex)
                 visibleLines.add(0, measuredLine)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt
index 50dcf3e..bd6578d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt
@@ -59,9 +59,11 @@
         // state would be lost and overridden with zeros.
         if (hadFirstNotEmptyLayout || measureResult.totalItemsCount > 0) {
             hadFirstNotEmptyLayout = true
+            val scrollOffset = measureResult.firstVisibleLineScrollOffset
+            check(scrollOffset >= 0f) { "scrollOffset should be non-negative ($scrollOffset)" }
             update(
                 ItemIndex(measureResult.firstVisibleLine?.items?.firstOrNull()?.index?.value ?: 0),
-                measureResult.firstVisibleLineScrollOffset
+                scrollOffset
             )
         }
     }
@@ -71,7 +73,7 @@
      * composing the items during the next measure pass and will be updated by the real
      * position calculated during the measurement. This means that there is guarantee that
      * exactly this index and offset will be applied as it is possible that:
-     * a) there will no item at this index in reality
+     * a) there will be no item at this index in reality
      * b) item at this index will be smaller than the asked scrollOffset, which means we would
      * switch to the next item
      * c) there will be not enough items to fill the viewport after the requested index, so we
@@ -96,7 +98,6 @@
 
     private fun update(index: ItemIndex, scrollOffset: Int) {
         require(index.value >= 0f) { "Index should be non-negative (${index.value})" }
-        require(scrollOffset >= 0f) { "scrollOffset should be non-negative ($scrollOffset)" }
         if (index != this.index) {
             this.index = index
             indexState.value = index.value
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrolling.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrolling.kt
index 52133b7..d14ce2fa 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrolling.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrolling.kt
@@ -16,19 +16,24 @@
 
 package androidx.compose.foundation.lazy.grid
 
-import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.AnimationVector1D
 import androidx.compose.animation.core.animateTo
-import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.copy
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.lazy.LazyGridItemInfo
 import androidx.compose.foundation.lazy.LazyGridState
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastFirstOrNull
 import kotlin.coroutines.cancellation.CancellationException
+import kotlin.math.abs
+import kotlin.math.max
 
 @OptIn(ExperimentalFoundationApi::class)
-private class ItemFoundInScroll(val item: LazyGridItemInfo) : CancellationException()
+private class ItemFoundInScroll(
+    val item: LazyGridItemInfo,
+    val previousAnimation: AnimationState<Float, AnimationVector1D>
+) : CancellationException()
 
 private val TargetDistance = 2500.dp
 private val BoundDistance = 1500.dp
@@ -36,42 +41,94 @@
 private const val DEBUG = false
 private inline fun debugLog(generateMsg: () -> String) {
     if (DEBUG) {
-        println("LazyListScrolling: ${generateMsg()}")
+        println("LazyGridScrolling: ${generateMsg()}")
     }
 }
 
 @OptIn(ExperimentalFoundationApi::class)
 internal suspend fun LazyGridState.doSmoothScrollToItem(
     index: Int,
-    scrollOffset: Int
+    scrollOffset: Int,
+    slotsPerLine: Int
 ) {
-    val animationSpec: AnimationSpec<Float> = spring()
+    require(index >= 0f) { "Index should be non-negative ($index)" }
     fun getTargetItem() = layoutInfo.visibleItemsInfo.fastFirstOrNull {
         it.index == index
     }
     scroll {
-        val targetDistancePx = with(density) { TargetDistance.toPx() }
-        val boundDistancePx = with(density) { BoundDistance.toPx() }
-        var loop = true
-        var prevVelocity = 0f
         try {
+            val targetDistancePx = with(density) { TargetDistance.toPx() }
+            val boundDistancePx = with(density) { BoundDistance.toPx() }
+            var loop = true
+            var anim = AnimationState(0f)
             val targetItemInitialInfo = getTargetItem()
             if (targetItemInitialInfo != null) {
                 // It's already visible, just animate directly
-                throw ItemFoundInScroll(targetItemInitialInfo)
+                throw ItemFoundInScroll(
+                    targetItemInitialInfo,
+                    anim
+                )
             }
             val forward = index > firstVisibleItemIndex
-            val target = if (forward) targetDistancePx else -targetDistancePx
+
+            fun isOvershot(): Boolean {
+                // Did we scroll past the item?
+                @Suppress("RedundantIf") // It's way easier to understand the logic this way
+                return if (forward) {
+                    if (firstVisibleItemIndex > index) {
+                        true
+                    } else if (
+                        firstVisibleItemIndex == index &&
+                        firstVisibleItemScrollOffset > scrollOffset
+                    ) {
+                        true
+                    } else {
+                        false
+                    }
+                } else { // backward
+                    if (firstVisibleItemIndex < index) {
+                        true
+                    } else if (
+                        firstVisibleItemIndex == index &&
+                        firstVisibleItemScrollOffset < scrollOffset
+                    ) {
+                        true
+                    } else {
+                        false
+                    }
+                }
+            }
+
             var loops = 1
-            while (loop) {
-                val anim = AnimationState(
-                    initialValue = 0f,
-                    initialVelocity = prevVelocity
+            while (loop && layoutInfo.totalItemsCount > 0) {
+                val visibleItems = layoutInfo.visibleItemsInfo
+                val averageLineMainAxisSize = calculateLineAverageMainAxisSize(
+                    visibleItems,
+                    true // TODO(b/191238807)
                 )
+                val before = index < firstVisibleItemIndex
+                val linesDiff =
+                    (index - firstVisibleItemIndex + (slotsPerLine - 1) * if (before) -1 else 1) /
+                        slotsPerLine
+
+                val expectedDistance = (averageLineMainAxisSize * linesDiff).toFloat() +
+                    scrollOffset - firstVisibleItemScrollOffset
+                val target = if (abs(expectedDistance) < targetDistancePx) {
+                    expectedDistance
+                } else {
+                    if (forward) targetDistancePx else -targetDistancePx
+                }
+
+                debugLog {
+                    "Scrolling to index=$index offset=$scrollOffset from " +
+                        "index=$firstVisibleItemIndex offset=$firstVisibleItemScrollOffset with " +
+                        "averageSize=$averageLineMainAxisSize and calculated target=$target"
+                }
+
+                anim = anim.copy(value = 0f)
                 var prevValue = 0f
                 anim.animateTo(
                     target,
-                    animationSpec = animationSpec,
                     sequentialAnimation = (anim.velocity != 0f)
                 ) {
                     // If we haven't found the item yet, check if it's visible.
@@ -93,7 +150,7 @@
                         targetItem = getTargetItem()
                         if (targetItem != null) {
                             debugLog { "Found the item after performing scrollBy()" }
-                        } else {
+                        } else if (!isOvershot()) {
                             if (delta != consumed) {
                                 debugLog { "Hit end without finding the item" }
                                 cancelAnimation()
@@ -117,11 +174,11 @@
                             if (forward) {
                                 if (
                                     loops >= 2 &&
-                                    index - layoutInfo.visibleItemsInfo.last().index > 100
+                                    index - layoutInfo.visibleItemsInfo.last().index > 200
                                 ) {
                                     // Teleport
                                     debugLog { "Teleport forward" }
-                                    snapToItemIndexInternal(index = index - 100, scrollOffset = 0)
+                                    snapToItemIndexInternal(index = index - 200, scrollOffset = 0)
                                 }
                             } else {
                                 if (
@@ -130,40 +187,15 @@
                                 ) {
                                     // Teleport
                                     debugLog { "Teleport backward" }
-                                    snapToItemIndexInternal(index = index + 100, scrollOffset = 0)
+                                    snapToItemIndexInternal(index = index + 200, scrollOffset = 0)
                                 }
                             }
                         }
                     }
-                    // Did we scroll past the item?
-                    @Suppress("RedundantIf") // It's way easier to understand the logic this way
-                    val overshot = if (forward) {
-                        if (firstVisibleItemIndex > index) {
-                            true
-                        } else if (
-                            firstVisibleItemIndex == index &&
-                            firstVisibleItemScrollOffset > scrollOffset
-                        ) {
-                            true
-                        } else {
-                            false
-                        }
-                    } else { // backward
-                        if (firstVisibleItemIndex < index) {
-                            true
-                        } else if (
-                            firstVisibleItemIndex == index &&
-                            firstVisibleItemScrollOffset < scrollOffset
-                        ) {
-                            true
-                        } else {
-                            false
-                        }
-                    }
 
                     // We don't throw ItemFoundInScroll when we snap, because once we've snapped to
                     // the final position, there's no need to animate to it.
-                    if (overshot) {
+                    if (isOvershot()) {
                         debugLog { "Overshot" }
                         snapToItemIndexInternal(index = index, scrollOffset = scrollOffset)
                         loop = false
@@ -171,25 +203,24 @@
                         return@animateTo
                     } else if (targetItem != null) {
                         debugLog { "Found item" }
-                        throw ItemFoundInScroll(targetItem)
+                        throw ItemFoundInScroll(
+                            targetItem,
+                            anim
+                        )
                     }
                 }
 
-                prevVelocity = anim.velocity
                 loops++
             }
         } catch (itemFound: ItemFoundInScroll) {
             // We found it, animate to it
             // Bring to the requested position - will be automatically stopped if not possible
-            val anim = AnimationState(
-                initialValue = 0f,
-                initialVelocity = prevVelocity
-            )
-            // TODO(popam): hardcoded .y
+            val anim = itemFound.previousAnimation.copy(value = 0f)
+            // TODO(b/191238807)
             val target = (itemFound.item.offset.y + scrollOffset).toFloat()
             var prevValue = 0f
             debugLog {
-                "Seeking by $target at velocity $prevVelocity, sequential: ${anim.velocity != 0f}"
+                "Seeking by $target at velocity ${itemFound.previousAnimation.velocity}"
             }
             anim.animateTo(target, sequentialAnimation = (anim.velocity != 0f)) {
                 // Springs can overshoot their target, clamp to the desired range
@@ -222,4 +253,48 @@
             snapToItemIndexInternal(index = index, scrollOffset = scrollOffset)
         }
     }
-}
\ No newline at end of file
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private fun calculateLineAverageMainAxisSize(
+    visibleItems: List<LazyGridItemInfo>,
+    isVertical: Boolean
+): Int {
+    val lineOf: (Int) -> Int = {
+        if (isVertical) visibleItems[it].row else visibleItems[it].column
+    }
+
+    var totalLinesMainAxisSize = 0
+    var linesCount = 0
+
+    var lineStartIndex = 0
+    while (lineStartIndex < visibleItems.size) {
+        val currentLine = lineOf(lineStartIndex)
+        if (currentLine == -1) {
+            // Filter out exiting items.
+            ++lineStartIndex
+            continue
+        }
+
+        var lineMainAxisSize = 0
+        var lineEndIndex = lineStartIndex
+        while (lineEndIndex < visibleItems.size && lineOf(lineEndIndex) == currentLine) {
+            lineMainAxisSize = max(
+                lineMainAxisSize,
+                if (isVertical) {
+                    visibleItems[lineEndIndex].size.height
+                } else {
+                    visibleItems[lineEndIndex].size.width
+                }
+            )
+            ++lineEndIndex
+        }
+
+        totalLinesMainAxisSize += lineMainAxisSize
+        ++linesCount
+
+        lineStartIndex = lineEndIndex
+    }
+
+    return totalLinesMainAxisSize / linesCount
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoView.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoView.kt
new file mode 100644
index 0000000..35aa72c
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoView.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.compose.foundation.relocation
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.OnGloballyPositionedModifier
+import androidx.compose.ui.modifier.ModifierLocalConsumer
+import androidx.compose.ui.modifier.ModifierLocalReadScope
+import androidx.compose.ui.modifier.modifierLocalOf
+
+/**
+ * The Key for the ModifierLocal that can be used to access the [BringIntoViewParent].
+ */
+@OptIn(ExperimentalFoundationApi::class)
+internal val ModifierLocalBringIntoViewParent = modifierLocalOf<BringIntoViewParent?> { null }
+
+/**
+ * Platform-specific "root" of the [BringIntoViewParent] chain to call into when there are no
+ * [ModifierLocalBringIntoViewParent]s above a [BringIntoViewChildModifier]. The value returned by
+ * this function should be passed to the [BringIntoViewChildModifier] constructor.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+internal expect fun rememberDefaultBringIntoViewParent(): BringIntoViewParent
+
+/**
+ * A node that can respond to [bringChildIntoView] requests from its children by scrolling its
+ * content.
+ */
+internal fun interface BringIntoViewParent {
+    /**
+     * Scrolls this node's content so that [rect] will be in visible bounds. Must ensure that the
+     * request is propagated up to the parent node.
+     *
+     * @param rect The rectangle to bring into view, relative to [childCoordinates].
+     * @param childCoordinates The [LayoutCoordinates] of the child node making the request. This
+     * parent can use these [LayoutCoordinates] to translate [rect] into its own coordinates.
+     */
+    suspend fun bringChildIntoView(rect: Rect, childCoordinates: LayoutCoordinates)
+}
+
+/**
+ * Common modifier logic shared between both requester and responder modifiers, namely recording
+ * the [LayoutCoordinates] of the modifier and providing access to the appropriate
+ * [BringIntoViewParent]: either one read from the [ModifierLocalBringIntoViewParent], or if no
+ * modifier local is specified then the [defaultParent].
+ *
+ * @param defaultParent The [BringIntoViewParent] to use if there is no
+ * [ModifierLocalBringIntoViewParent] available to read. This parent should always be obtained by
+ * calling [rememberDefaultBringIntoViewParent] to support platform-specific integration.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+internal abstract class BringIntoViewChildModifier(
+    private val defaultParent: BringIntoViewParent
+) : ModifierLocalConsumer,
+    OnGloballyPositionedModifier {
+
+    private var localParent: BringIntoViewParent? = null
+
+    /** The [LayoutCoordinates] of this modifier, if attached. */
+    protected var layoutCoordinates: LayoutCoordinates? = null
+        get() = field?.takeIf { it.isAttached }
+        private set
+
+    protected val parent: BringIntoViewParent
+        get() = localParent ?: defaultParent
+
+    override fun onModifierLocalsUpdated(scope: ModifierLocalReadScope) {
+        with(scope) {
+            localParent = ModifierLocalBringIntoViewParent.current
+        }
+    }
+
+    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+        layoutCoordinates = coordinates
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
index 786a038..3286e49 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
@@ -17,20 +17,13 @@
 package androidx.compose.foundation.relocation
 
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.relocation.BringIntoViewResponder.Companion.ModifierLocalBringIntoViewResponder
-import androidx.compose.foundation.relocation.BringIntoViewResponder.Companion.RootBringIntoViewResponder
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collection.MutableVector
 import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.geometry.toRect
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.modifier.ModifierLocalConsumer
-import androidx.compose.ui.modifier.ModifierLocalReadScope
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.toSize
 
@@ -98,59 +91,55 @@
 @ExperimentalFoundationApi
 fun Modifier.bringIntoViewRequester(
     bringIntoViewRequester: BringIntoViewRequester
-): Modifier = composed(
-    inspectorInfo = debugInspectorInfo {
-        name = "bringIntoViewRequester"
-        properties["bringIntoViewRequester"] = bringIntoViewRequester
+): Modifier = composed(debugInspectorInfo {
+    name = "bringIntoViewRequester"
+    properties["bringIntoViewRequester"] = bringIntoViewRequester
+}) {
+    val defaultResponder = rememberDefaultBringIntoViewParent()
+    val modifier = remember(defaultResponder) {
+        BringIntoViewRequesterModifier(defaultResponder)
     }
-) {
-    val bringIntoViewData = remember { BringIntoViewData(BringRectangleOnScreenRequester()) }
     if (bringIntoViewRequester is BringIntoViewRequesterImpl) {
         DisposableEffect(bringIntoViewRequester) {
-            bringIntoViewRequester.bringIntoViewUsages += bringIntoViewData
-            onDispose { bringIntoViewRequester.bringIntoViewUsages -= bringIntoViewData }
+            bringIntoViewRequester.modifiers += modifier
+            onDispose { bringIntoViewRequester.modifiers -= modifier }
         }
     }
-
-    Modifier
-        .bringRectangleOnScreenRequester(bringIntoViewData.bringRectangleOnScreenRequester)
-        .onGloballyPositioned { bringIntoViewData.layoutCoordinates = it }
-        .then(
-            remember {
-                object : ModifierLocalConsumer {
-                    override fun onModifierLocalsUpdated(scope: ModifierLocalReadScope) {
-                        bringIntoViewData.parent = scope.run {
-                            ModifierLocalBringIntoViewResponder.current
-                        }
-                    }
-                }
-            }
-        )
+    return@composed modifier
 }
 
 @ExperimentalFoundationApi
 private class BringIntoViewRequesterImpl : BringIntoViewRequester {
-    val bringIntoViewUsages: MutableVector<BringIntoViewData> = mutableVectorOf()
+    val modifiers = mutableVectorOf<BringIntoViewRequesterModifier>()
 
     override suspend fun bringIntoView(rect: Rect?) {
-        bringIntoViewUsages.forEach {
-            val layoutCoordinates = it.layoutCoordinates
-            if (layoutCoordinates == null || !layoutCoordinates.isAttached) return@forEach
-
-            // If the rect is not specified, use a rectangle representing the entire composable.
-            val sourceRect = rect ?: layoutCoordinates.size.toSize().toRect()
-
-            // Convert the rect into parent coordinates.
-            val rectInParentCoordinates = it.parent.toLocalRect(sourceRect, layoutCoordinates)
-
-            it.parent.bringIntoView(rectInParentCoordinates, it.bringRectangleOnScreenRequester)
+        modifiers.forEach {
+            it.bringIntoView(rect)
         }
     }
 }
 
+/**
+ * A modifier that holds state and modifier implementations for [bringIntoViewRequester]. It has
+ * access to the next [BringIntoViewParent] via [BringIntoViewChildModifier], and uses that parent
+ * to respond to requests to [bringIntoView].
+ */
 @ExperimentalFoundationApi
-private data class BringIntoViewData(
-    val bringRectangleOnScreenRequester: BringRectangleOnScreenRequester,
-    var parent: BringIntoViewResponder = RootBringIntoViewResponder,
-    var layoutCoordinates: LayoutCoordinates? = null
-)
+private class BringIntoViewRequesterModifier(
+    defaultParent: BringIntoViewParent
+) : BringIntoViewChildModifier(defaultParent) {
+
+    /**
+     * Requests that [rect] (if non-null) or the entire bounds of this modifier's node (if [rect]
+     * is null) be brought into view by the [parent]&nbsp;[BringIntoViewParent].
+     */
+    suspend fun bringIntoView(rect: Rect?) {
+        val layoutCoordinates = layoutCoordinates ?: return
+
+        // If the rect is not specified, use a rectangle representing the entire composable.
+        val sourceRect = rect ?: layoutCoordinates.size.toSize().toRect()
+
+        // Convert the rect into parent coordinates.
+        parent.bringChildIntoView(sourceRect, layoutCoordinates)
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
index 85a9e2e..6d72cd38 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
@@ -17,85 +17,152 @@
 package androidx.compose.foundation.relocation
 
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.relocation.BringIntoViewResponder.Companion.ModifierLocalBringIntoViewResponder
-import androidx.compose.foundation.relocation.BringIntoViewResponder.Companion.RootBringIntoViewResponder
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.modifier.ModifierLocalProvider
 import androidx.compose.ui.modifier.ProvidableModifierLocal
-import androidx.compose.ui.modifier.modifierLocalOf
+import androidx.compose.ui.platform.debugInspectorInfo
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
 
 /**
- * A parent that can respond to [bringIntoView] requests from its children, and scroll so that the
- * item is visible on screen.
+ * A parent that can respond to [bringChildIntoView] requests from its children, and scroll so that
+ * the item is visible on screen. To apply a responder to an element, pass it to the
+ * [bringIntoViewResponder] modifier.
  *
  * When a component calls [BringIntoViewRequester.bringIntoView], the
- * [BringIntoView ModifierLocal][ModifierLocalBringIntoViewResponder] is read to gain access to the
- * [BringIntoViewResponder], which is responsible for performing a scroll to bring the requesting
- * component into view and then send a [bringIntoView] request to its parent.
+ * [BringIntoView ModifierLocal][ModifierLocalBringIntoViewParent] is read to gain access to the
+ * [BringIntoViewResponder], which is responsible for, in order:
  *
+ * 1. Calculating a rectangle that its parent responder should bring into view by returning it from
+ *    [calculateRectForParent].
+ * 2. Performing any scroll or other layout adjustments needed to ensure the requested rectangle is
+ *    brought into view in [bringChildIntoView].
+ *
+ * Here is a sample defining a custom [BringIntoViewResponder]:
+ * @sample androidx.compose.foundation.samples.BringIntoViewResponderSample
+ *
+ * Here is a sample where a composable is brought into view:
  * @sample androidx.compose.foundation.samples.BringIntoViewSample
  *
+ * Here is a sample where a part of a composable is brought into view:
+ * @sample androidx.compose.foundation.samples.BringPartOfComposableIntoViewSample
+ *
  * @see BringIntoViewRequester
  */
 @ExperimentalFoundationApi
 interface BringIntoViewResponder {
+
     /**
-     * Bring this specified rectangle into bounds by making all the scrollable parents scroll
+     * Return the rectangle in this node that should be brought into view by this node's parent,
+     * in coordinates relative to this node. If this node needs to adjust itself to bring
+     * [localRect] into view, the returned rectangle should be the destination rectangle that
+     * [localRect] will eventually occupy once this node's content is adjusted.
+     *
+     * @param localRect The rectangle that should be brought into view, relative to this node. This
+     * will be the same rectangle passed to [bringChildIntoView].
+     * @return The rectangle in this node that should be brought into view itself, relative to this
+     * node. If this node needs to scroll to bring [localRect] into view, the returned rectangle
+     * should be the destination rectangle that [localRect] will eventually occupy, once the
+     * scrolling animation is finished.
+     */
+    @ExperimentalFoundationApi
+    fun calculateRectForParent(localRect: Rect): Rect
+
+    /**
+     * Bring this specified rectangle into bounds by making this scrollable parent scroll
      * appropriately.
      *
-     * @param rect The rectangle that should be brought into view. If you
-     * don't specify the coordinates, the entire component is brought into view.
-     *
-     *
-     * Here is a sample where a composable is brought into view:
-     * @sample androidx.compose.foundation.samples.BringIntoViewSample
-     *
-     * Here is a sample where a part of a composable is brought into view:
-     * @sample androidx.compose.foundation.samples.BringPartOfComposableIntoViewSample
+     * @param localRect The rectangle that should be brought into view, relative to this node. This
+     * is the same rectangle that will have been passed to [calculateRectForParent].
      */
-    suspend fun bringIntoView(rect: Rect)
+    @ExperimentalFoundationApi
+    suspend fun bringChildIntoView(localRect: Rect)
+}
+
+/**
+ * A parent that can respond to [BringIntoViewRequester] requests from its children, and scroll so
+ * that the item is visible on screen. See [BringIntoViewResponder] for more details about how
+ * this mechanism works.
+ *
+ * @sample androidx.compose.foundation.samples.BringIntoViewResponderSample
+ * @sample androidx.compose.foundation.samples.BringIntoViewSample
+ *
+ * @see BringIntoViewRequester
+ */
+@ExperimentalFoundationApi
+fun Modifier.bringIntoViewResponder(
+    responder: BringIntoViewResponder
+): Modifier = composed(debugInspectorInfo {
+    name = "bringIntoViewResponder"
+    properties["responder"] = responder
+}) {
+    val defaultParent = rememberDefaultBringIntoViewParent()
+    val modifier = remember(defaultParent) {
+        BringIntoViewResponderModifier(defaultParent)
+    }
+    modifier.responder = responder
+    return@composed modifier
+}
+
+/**
+ * A modifier that holds state and modifier implementations for [bringIntoViewResponder]. It has
+ * access to the next [BringIntoViewParent] via [BringIntoViewChildModifier] and additionally
+ * provides itself as the [BringIntoViewParent] for subsequent modifiers. This class is responsible
+ * for recursively propagating requests up the responder chain.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+private class BringIntoViewResponderModifier(
+    defaultParent: BringIntoViewParent
+) : BringIntoViewChildModifier(defaultParent),
+    ModifierLocalProvider<BringIntoViewParent?>,
+    BringIntoViewParent {
+
+    lateinit var responder: BringIntoViewResponder
+
+    override val key: ProvidableModifierLocal<BringIntoViewParent?>
+        get() = ModifierLocalBringIntoViewParent
+    override val value: BringIntoViewParent
+        get() = this
 
     /**
-     * Convert a Rect into the layoutCoordinates of this [BringIntoViewResponder].
+     * Responds to a child's request by first converting [rect] into this node's [LayoutCoordinates]
+     * and then, concurrently, calling the [responder] and the [parent] to handle the request.
      */
-    fun toLocalRect(rect: Rect, layoutCoordinates: LayoutCoordinates): Rect
+    override suspend fun bringChildIntoView(rect: Rect, childCoordinates: LayoutCoordinates) {
+        val layoutCoordinates = layoutCoordinates ?: return
+        if (!childCoordinates.isAttached) return
+        val localRect = layoutCoordinates.localRectOf(childCoordinates, rect)
+        val parentRect = responder.calculateRectForParent(localRect)
 
-    @ExperimentalFoundationApi
-    companion object {
-        /**
-         * The Key for the ModifierLocal that can be used to access the [BringIntoViewResponder].
-         */
-        val ModifierLocalBringIntoViewResponder: ProvidableModifierLocal<BringIntoViewResponder> =
-            modifierLocalOf { RootBringIntoViewResponder }
+        // For the item to be visible, if needs to be in the viewport of all its ancestors.
+        // Note: For now we run both of these concurrently, but in the future we could make this
+        // configurable. (The child relocation could be executed before the parent, or parent
+        // before the child).
+        coroutineScope {
+            // Bring the requested Child into this parent's view.
+            launch {
+                responder.bringChildIntoView(localRect)
+            }
 
-        /**
-         * The Root [BringIntoViewResponder]. If you read this value for the
-         * [ModifierLocalBringIntoViewResponder], it means that this is the topmost
-         * [BringIntoViewResponder] in this hierarchy.
-         */
-        val RootBringIntoViewResponder = object : BringIntoViewResponder {
-            override suspend fun bringIntoView(rect: Rect) {}
-            override fun toLocalRect(rect: Rect, layoutCoordinates: LayoutCoordinates) =
-                Rect(layoutCoordinates.localToRoot(rect.topLeft), rect.size)
+            parent.bringChildIntoView(parentRect, layoutCoordinates)
         }
     }
 }
 
 /**
- * Brings [rectInParentCoordinates] into view by either calling this [BringIntoViewResponder] or,
- * if this is the [RootBringIntoViewResponder], then it delegates to
- * [bringRectangleOnScreenRequester]. Implementations of [BringIntoViewResponder] should call this
- * to propagate the request to their parent to ensure that the request is actually propagated.
+ * Translates [rect], specified in [sourceCoordinates], into this [LayoutCoordinates].
  */
-@OptIn(ExperimentalFoundationApi::class)
-internal suspend fun BringIntoViewResponder.bringIntoView(
-    rectInParentCoordinates: Rect,
-    bringRectangleOnScreenRequester: BringRectangleOnScreenRequester
-) {
-    if (this == RootBringIntoViewResponder) {
-        // Use the platform specific API to bring the rectangle on screen.
-        bringRectangleOnScreenRequester.bringRectangleOnScreen(rectInParentCoordinates)
-    } else {
-        bringIntoView(rectInParentCoordinates)
-    }
-}
+private fun LayoutCoordinates.localRectOf(
+    sourceCoordinates: LayoutCoordinates,
+    rect: Rect
+): Rect {
+    // Translate the supplied layout coordinates into the coordinate system of this parent.
+    val localRect = localBoundingBoxOf(sourceCoordinates, clipBounds = false)
+
+    // Translate the rect to this parent's local coordinates.
+    return rect.translate(localRect.topLeft)
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringRectangleOnScreen.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringRectangleOnScreen.kt
deleted file mode 100644
index f80d946..0000000
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringRectangleOnScreen.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.relocation
-
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Rect
-
-/**
- * Platform specific internal API to bring a rectangle into view.
- */
-internal expect class BringRectangleOnScreenRequester() {
-    fun bringRectangleOnScreen(rect: Rect)
-}
-
-/**
- * Companion Modifier to [BringRectangleOnScreenRequester].
- */
-internal expect fun Modifier.bringRectangleOnScreenRequester(
-    bringRectangleOnScreenRequester: BringRectangleOnScreenRequester
-): Modifier
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/relocation/BringRectangleOnScreen.desktop.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.desktop.kt
similarity index 64%
rename from compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/relocation/BringRectangleOnScreen.desktop.kt
rename to compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.desktop.kt
index 93e1cdd..ea02955 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/relocation/BringRectangleOnScreen.desktop.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.desktop.kt
@@ -16,24 +16,17 @@
 
 package androidx.compose.foundation.relocation
 
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Rect as ComposeRect
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.runtime.Composable
 
 /**
  * Platform specific internal API to bring a rectangle into view.
  */
-internal actual class BringRectangleOnScreenRequester {
-    @Suppress("UNUSED_PARAMETER")
-    actual fun bringRectangleOnScreen(rect: ComposeRect) {
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+internal actual fun rememberDefaultBringIntoViewParent(): BringIntoViewParent {
+    return BringIntoViewParent { _, _ ->
         // TODO(b/203204124): Implement this if desktop has a
         //  concept similar to Android's View.requestRectangleOnScreen.
     }
-}
-
-/**
- * Companion Modifier to [BringRectangleOnScreenRequester].
- */
-@Suppress("UNUSED_PARAMETER")
-internal actual fun Modifier.bringRectangleOnScreenRequester(
-    bringRectangleOnScreenRequester: BringRectangleOnScreenRequester
-): Modifier = this
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/compose/ui/ui-test-junit4/build.gradle b/compose/ui/ui-test-junit4/build.gradle
index 987464d3..f0ee239 100644
--- a/compose/ui/ui-test-junit4/build.gradle
+++ b/compose/ui/ui-test-junit4/build.gradle
@@ -62,6 +62,7 @@
 
         androidTestImplementation(project(":compose:test-utils"))
         androidTestImplementation(project(":compose:material:material"))
+        androidTestImplementation("androidx.fragment:fragment-testing:1.4.1")
         androidTestImplementation(libs.testRules)
         androidTestImplementation(libs.testRunner)
         androidTestImplementation(libs.truth)
@@ -124,6 +125,7 @@
             androidAndroidTest.dependencies {
                 implementation(project(":compose:test-utils"))
                 implementation(project(":compose:material:material"))
+                implementation("androidx.fragment:fragment-testing:1.4.1")
                 implementation(libs.testRules)
                 implementation(libs.testRunner)
                 implementation(libs.truth)
diff --git a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeInFragmentTest.kt b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeInFragmentTest.kt
new file mode 100644
index 0000000..5433960
--- /dev/null
+++ b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeInFragmentTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.test.junit4
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import androidx.compose.material.Text
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.test.onNodeWithText
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.testing.launchFragmentInContainer
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class ComposeInFragmentTest {
+    @get:Rule
+    val rule = createEmptyComposeRule()
+
+    @Test
+    fun test() {
+        launchFragmentInContainer<CustomFragment>()
+        rule.onNodeWithText("Hello Compose").assertExists()
+    }
+
+    class CustomFragment : Fragment() {
+        override fun onCreateView(
+            inflater: LayoutInflater,
+            container: ViewGroup?,
+            savedInstanceState: Bundle?
+        ): View? {
+            return container?.let {
+                ComposeView(container.context).apply {
+                    layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
+                }
+            }
+        }
+
+        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+            (view as ComposeView).setContent {
+                Text("Hello Compose")
+            }
+        }
+    }
+}
diff --git a/compose/ui/ui-text-google-fonts/api/current.txt b/compose/ui/ui-text-google-fonts/api/current.txt
new file mode 100644
index 0000000..925f1b8
--- /dev/null
+++ b/compose/ui/ui-text-google-fonts/api/current.txt
@@ -0,0 +1,8 @@
+// Signature format: 4.0
+package androidx.compose.ui.text.googlefonts {
+
+  public final class GoogleFontKt {
+  }
+
+}
+
diff --git a/compose/ui/ui-text-google-fonts/api/public_plus_experimental_current.txt b/compose/ui/ui-text-google-fonts/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..882734b
--- /dev/null
+++ b/compose/ui/ui-text-google-fonts/api/public_plus_experimental_current.txt
@@ -0,0 +1,13 @@
+// Signature format: 4.0
+package androidx.compose.ui.text.googlefonts {
+
+  public final class GoogleFontKt {
+    method @androidx.compose.ui.text.ExperimentalTextApi public static androidx.compose.ui.text.font.Font GoogleFont(String name, androidx.compose.ui.text.googlefonts.GoogleFontProvider fontProvider, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional boolean bestEffort);
+  }
+
+  @androidx.compose.ui.text.ExperimentalTextApi public final class GoogleFontProvider {
+    ctor public GoogleFontProvider(String providerAuthority, String providerPackage, java.util.List<? extends java.util.List<byte[]>> certificates);
+  }
+
+}
+
diff --git a/compose/ui/ui-text-google-fonts/api/res-current.txt b/compose/ui/ui-text-google-fonts/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/compose/ui/ui-text-google-fonts/api/res-current.txt
diff --git a/compose/ui/ui-text-google-fonts/api/restricted_current.txt b/compose/ui/ui-text-google-fonts/api/restricted_current.txt
new file mode 100644
index 0000000..925f1b8
--- /dev/null
+++ b/compose/ui/ui-text-google-fonts/api/restricted_current.txt
@@ -0,0 +1,8 @@
+// Signature format: 4.0
+package androidx.compose.ui.text.googlefonts {
+
+  public final class GoogleFontKt {
+  }
+
+}
+
diff --git a/compose/ui/ui-text-google-fonts/build.gradle b/compose/ui/ui-text-google-fonts/build.gradle
new file mode 100644
index 0000000..a70217c
--- /dev/null
+++ b/compose/ui/ui-text-google-fonts/build.gradle
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("AndroidXComposePlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    kotlinPlugin(project(":compose:compiler:compiler"))
+
+    implementation(libs.kotlinStdlib)
+
+    implementation(project(":compose:runtime:runtime"))
+    implementation(project(":compose:ui:ui-text"))
+    implementation(project(":core:core"))
+
+    androidTestImplementation(project(":compose:ui:ui-test-junit4"))
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.truth)
+}
+
+androidx {
+    name = "AndroidX Compose Google Fonts integration"
+    type = LibraryType.PUBLISHED_LIBRARY
+    mavenGroup = LibraryGroups.COMPOSE_UI
+    inceptionYear = "2022"
+    description = "Compose Downloadable Fonts integration for Google Fonts"
+}
diff --git a/compose/ui/ui-text-google-fonts/src/androidTest/AndroidManifest.xml b/compose/ui/ui-text-google-fonts/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..f8bb6461
--- /dev/null
+++ b/compose/ui/ui-text-google-fonts/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?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 package="androidx.compose.ui.text.googlefonts" />
diff --git a/compose/ui/ui-text-google-fonts/src/androidTest/java/androidx/compose/ui/text/googlefonts/GoogleFontTest.kt b/compose/ui/ui-text-google-fonts/src/androidTest/java/androidx/compose/ui/text/googlefonts/GoogleFontTest.kt
new file mode 100644
index 0000000..e3a09c0
--- /dev/null
+++ b/compose/ui/ui-text-google-fonts/src/androidTest/java/androidx/compose/ui/text/googlefonts/GoogleFontTest.kt
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.googlefonts
+
+import android.content.Context
+import android.graphics.Typeface
+import android.os.Handler
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.font.AndroidFont
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontLoadingStrategy
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.core.provider.FontRequest
+import androidx.core.provider.FontsContractCompat
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
+import kotlinx.coroutines.test.runBlockingTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalTextApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class GoogleFontTest {
+
+    val context = InstrumentationRegistry.getInstrumentation().context
+
+    private val TestProvider = GoogleFontProvider(
+        "providerAuthority",
+        "providerPackage",
+        listOf(listOf(ByteArray(100) { it.toByte() }))
+    )
+
+    @Test
+    fun GoogleFont_create_ComposeFont() {
+        val font = GoogleFont("Test font", TestProvider)
+        assertThat(font).isInstanceOf(Font::class.java)
+    }
+
+    @OptIn(ExperimentalTextApi::class)
+    @Test
+    fun GoogleFont_is_AsyncFont() {
+        val font = GoogleFont("Test font", TestProvider)
+        assertThat(font.loadingStrategy).isEqualTo(FontLoadingStrategy.Async)
+    }
+
+    @Test
+    fun GoogleFont_default_W400() {
+        val font = GoogleFont("Test", TestProvider)
+        assertThat(font.weight).isEqualTo(FontWeight.W400)
+    }
+
+    @Test
+    fun GoogleFont_default_isNormal() {
+        val font = GoogleFont("Test", TestProvider)
+        assertThat(font.style).isEqualTo(FontStyle.Normal)
+    }
+
+    @Test
+    fun GoogleFont_default_bestEffort_true() {
+        val font = GoogleFont("best effort", TestProvider) as GoogleFontImpl
+        assertThat(font.bestEffort).isTrue()
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun GoogleFont_throwsOn_emptyName() {
+        GoogleFont("", TestProvider)
+    }
+
+    @Test
+    fun GoogleFont_keepsUrlEncodingRequiredNames() {
+        val expected = "!@#$%^&*(){}'<>PYFGCRL?+|AOEUIDHTNS_:QJKXBMWVZ~~`1234567890[]/=\\-;:,."
+        val font = GoogleFont(expected, TestProvider) as GoogleFontImpl
+        assertThat(font.name).isEqualTo(expected)
+    }
+
+    @Test
+    fun GoogleFontImpl_fontRequest_containsName() {
+        val font = GoogleFont("Test Name", TestProvider) as GoogleFontImpl
+        assertThat(font.toFontRequest().query).contains("name=Test+Name")
+    }
+
+    @Test
+    fun GoogleFontImpl_fontRequest_containsWeight() {
+        val font = GoogleFont("a", TestProvider, weight = FontWeight.W800) as GoogleFontImpl
+        assertThat(font.toFontRequest().query).contains("weight=800")
+    }
+
+    @Test
+    fun GoogleFontImpl_fontRequest_containsStyle_normal() {
+        val font = GoogleFont("a", TestProvider) as GoogleFontImpl
+        assertThat(font.toFontRequest().query).contains("italic=0")
+    }
+
+    @Test
+    fun GoogleFontImpl_fontRequest_containsStyle_italic() {
+        val font = GoogleFont("a", TestProvider, style = FontStyle.Italic) as GoogleFontImpl
+        assertThat(font.toFontRequest().query).contains("italic=1")
+    }
+
+    @Test
+    fun GoogleFontImpl_fontRequest_bestEffort() {
+        val font = GoogleFont("a", TestProvider) as GoogleFontImpl
+        assertThat(font.toFontRequest().query).contains("besteffort=true")
+    }
+
+    @Test
+    fun GoogleFontImpl_fontRequest_bestEffort_false() {
+        val font = GoogleFont("a", TestProvider, bestEffort = false) as GoogleFontImpl
+        assertThat(font.toFontRequest().query).contains("besteffort=false")
+    }
+
+    @Test
+    fun GoogleFontImpl_providerAuthority_passedDown() {
+        val font = GoogleFont("a", TestProvider) as GoogleFontImpl
+        assertThat(font.toFontRequest().providerAuthority).isEqualTo(TestProvider.providerAuthority)
+    }
+
+    @Test
+    fun GoogleFontImpl_providerPackage_passedDown() {
+        val font = GoogleFont("a", TestProvider) as GoogleFontImpl
+        assertThat(font.toFontRequest().providerPackage).isEqualTo(TestProvider.providerPackage)
+    }
+
+    @Test
+    fun GoogleFontImpl_providerCerts_passedDown() {
+        val font = GoogleFont("a", TestProvider) as GoogleFontImpl
+        assertThat(font.toFontRequest().certificates).isEqualTo(TestProvider.certificates)
+    }
+
+    @Test
+    fun GoogleFontImpl_TypefaceStyle_Normal() {
+        val font = GoogleFont("a", TestProvider) as GoogleFontImpl
+        assertThat(font.toTypefaceStyle()).isEqualTo(Typeface.NORMAL)
+    }
+
+    @Test
+    fun GoogleFontImpl_TypefaceStyle_Italic() {
+        val font = GoogleFont("a", TestProvider, style = FontStyle.Italic) as GoogleFontImpl
+        assertThat(font.toTypefaceStyle()).isEqualTo(Typeface.ITALIC)
+    }
+
+    @Test
+    fun GoogleFontImpl_TypefaceStyle_Bold() {
+        val font = GoogleFont("a", TestProvider, weight = FontWeight.Bold) as GoogleFontImpl
+        assertThat(font.toTypefaceStyle()).isEqualTo(Typeface.BOLD)
+    }
+
+    @Test
+    fun GoogleFontImpl_TypefaceStyle_BoldItalic() {
+        val font = GoogleFont(
+            "a",
+            TestProvider,
+            weight = FontWeight.Bold,
+            style = FontStyle.Italic
+        ) as GoogleFontImpl
+        assertThat(font.toTypefaceStyle()).isEqualTo(Typeface.BOLD_ITALIC)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun GoogleFont_TypefaceLoader_resumesOnCompletion() {
+        val compatLoader = CapturingFontsContractCompatLoader()
+        runBlockingTest {
+            val deferred = async {
+                GoogleFontTypefaceLoader.awaitLoad(
+                    context,
+                    GoogleFont("Foo", TestProvider) as AndroidFont,
+                    compatLoader
+                )
+            }
+            compatLoader.callback?.onTypefaceRetrieved(Typeface.MONOSPACE)
+            assertThat(deferred.await()).isEqualTo(Typeface.MONOSPACE)
+        }
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun GoogleFont_TypefaceLoader_throwsOnError() {
+        val compatLoader = CapturingFontsContractCompatLoader()
+        runBlockingTest {
+            val deferred = async {
+                GoogleFontTypefaceLoader.awaitLoad(
+                    context,
+                    GoogleFont("Foo", TestProvider) as AndroidFont,
+                    compatLoader
+                )
+            }
+            compatLoader.callback?.onTypefaceRequestFailed(42)
+            var exception: IllegalStateException? = null
+            try {
+                assertThat(deferred.await())
+            } catch (ex: IllegalStateException) {
+                exception = ex
+            }
+            assertThat(exception?.message).contains("reason=42")
+            assertThat(exception?.message).contains("GoogleFont(name=\"Foo\"")
+        }
+    }
+
+    @Test
+    fun GoogleFont_toString() {
+        val font = GoogleFont("Font Family", TestProvider)
+        assertThat(font.toString()).isEqualTo(
+            "GoogleFont(name=\"Font Family\", weight=FontWeight(weight=400), " +
+                "style=Normal, bestEffort=true)"
+        )
+    }
+
+    private class CapturingFontsContractCompatLoader : FontsContractCompatLoader {
+        var callback: FontsContractCompat.FontRequestCallback? = null
+
+        override fun requestFont(
+            context: Context,
+            fontRequest: FontRequest,
+            typefaceStyle: Int,
+            handler: Handler,
+            callback: FontsContractCompat.FontRequestCallback
+        ) {
+            this.callback = callback
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-text-google-fonts/src/main/AndroidManifest.xml b/compose/ui/ui-text-google-fonts/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5597ebf
--- /dev/null
+++ b/compose/ui/ui-text-google-fonts/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<!--
+  Copyright 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest package="androidx.compose.ui.text.googlefonts" />
diff --git a/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/GoogleFont.kt b/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/GoogleFont.kt
new file mode 100644
index 0000000..e0a6ba9
--- /dev/null
+++ b/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/GoogleFont.kt
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// this file provides integration with fonts.google.com, which is called Google Fonts
+@file:Suppress("MentionsGoogle")
+
+package androidx.compose.ui.text.googlefonts
+
+import android.content.Context
+import android.graphics.Typeface
+import android.os.Handler
+import android.os.Looper
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.font.AndroidFont
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontLoadingStrategy
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.core.provider.FontRequest
+import androidx.core.provider.FontsContractCompat
+import java.lang.IllegalStateException
+import java.net.URLEncoder
+import java.nio.charset.StandardCharsets
+import kotlin.coroutines.resume
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+/**
+ * Load a font from Google Fonts via Downloadable Fonts.
+ *
+ * To learn more about the features supported by Google Fonts, see
+ * [Get Started with the Google Fonts for Android](https://developers.google.com/fonts/docs/android)
+ *
+ * @param name of font such as "Roboto" or "Open Sans"
+ * @param fontProvider configuration for downloadable font provider
+ * @param weight font weight to load, or weight to closest match if [bestEffort] is true
+ * @param style italic or normal font
+ * @param bestEffort If besteffort is true and your query specifies a valid family name but the
+ * requested width/weight/italic value is not supported Google Fonts will return the best match it
+ * can find within the family. If false, exact matches will be returned only.
+ */
+// contains Google in name because this function provides integration with fonts.google.com
+@Suppress("MentionsGoogle")
+@ExperimentalTextApi
+fun GoogleFont(
+    name: String,
+    fontProvider: GoogleFontProvider,
+    weight: FontWeight = FontWeight.W400,
+    style: FontStyle = FontStyle.Normal,
+    bestEffort: Boolean = true
+): Font {
+    require(name.isNotEmpty()) { "name cannot be empty" }
+    return GoogleFontImpl(
+        name = name,
+        fontProvider = fontProvider,
+        weight = weight,
+        style = style,
+        bestEffort = bestEffort
+    )
+}
+
+/**
+ * Attributes used to create a [FontRequest] for a [GoogleFont].
+ *
+ * @see FontRequest
+ */
+@ExperimentalTextApi
+// contains Google in name because this function provides integration with fonts.google.com
+@Suppress("MentionsGoogle")
+class GoogleFontProvider(
+    internal val providerAuthority: String,
+    internal val providerPackage: String,
+    internal val certificates: List<List<ByteArray>>
+) {
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as GoogleFontProvider
+
+        if (providerAuthority != other.providerAuthority) return false
+        if (providerPackage != other.providerPackage) return false
+        if (certificates != other.certificates) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = providerAuthority.hashCode()
+        result = 31 * result + providerPackage.hashCode()
+        result = 31 * result + certificates.hashCode()
+        return result
+    }
+}
+
+@ExperimentalTextApi
+internal data class GoogleFontImpl constructor(
+    val name: String,
+    private val fontProvider: GoogleFontProvider,
+    override val weight: FontWeight,
+    override val style: FontStyle,
+    val bestEffort: Boolean
+) : AndroidFont(FontLoadingStrategy.Async) {
+    override val typefaceLoader: TypefaceLoader
+        get() = GoogleFontTypefaceLoader
+
+    fun toFontRequest(): FontRequest {
+        val query = "name=${name.encode()}&weight=${weight.weight}" +
+            "&italic=${style.toQueryParam()}&besteffort=${bestEffortQueryParam()}"
+        return FontRequest(
+            fontProvider.providerAuthority,
+            fontProvider.providerPackage,
+            query,
+            fontProvider.certificates
+        )
+    }
+
+    private fun bestEffortQueryParam() = if (bestEffort) "true" else "false"
+    private fun FontStyle.toQueryParam(): Int = if (this == FontStyle.Italic) 1 else 0
+    private fun String.encode() = URLEncoder.encode(this, StandardCharsets.UTF_8.toString())
+
+    fun toTypefaceStyle(): Int {
+        val isItalic = style == FontStyle.Italic
+        val isBold = weight >= FontWeight.Bold
+        return when {
+            isItalic && isBold -> Typeface.BOLD_ITALIC
+            isItalic -> Typeface.ITALIC
+            isBold -> Typeface.BOLD
+            else -> Typeface.NORMAL
+        }
+    }
+
+    override fun toString(): String {
+        return "GoogleFont(name=\"$name\", weight=$weight, style=$style, bestEffort=$bestEffort)"
+    }
+}
+
+@ExperimentalTextApi
+internal object GoogleFontTypefaceLoader : AndroidFont.TypefaceLoader {
+    override fun loadBlocking(context: Context, font: AndroidFont): Typeface? {
+        error("GoogleFont only support async loading: $font")
+    }
+
+    override suspend fun awaitLoad(context: Context, font: AndroidFont): Typeface? {
+        return awaitLoad(context, font, DefaultFontsContractCompatLoader)
+    }
+
+    internal suspend fun awaitLoad(
+        context: Context,
+        font: AndroidFont,
+        loader: FontsContractCompatLoader
+    ): Typeface? {
+        require(font is GoogleFontImpl) { "Only GoogleFontImpl supported (actual $font)" }
+        val fontRequest = font.toFontRequest()
+        val typefaceStyle = font.toTypefaceStyle()
+
+        return suspendCancellableCoroutine { continuation ->
+            val callback = object : FontsContractCompat.FontRequestCallback() {
+                override fun onTypefaceRetrieved(typeface: Typeface?) {
+                    // this is entered from any thread
+                    continuation.resume(typeface)
+                }
+
+                override fun onTypefaceRequestFailed(reason: Int) {
+                    // this is entered from any thread
+                    continuation.cancel(
+                        IllegalStateException("Failed to load $font (reason=$reason)")
+                    )
+                }
+            }
+
+            loader.requestFont(
+                context = context,
+                fontRequest = fontRequest,
+                typefaceStyle = typefaceStyle,
+                handler = asyncHandlerForCurrentThreadOrMainIfNoLooper(),
+                callback = callback
+            )
+        }
+    }
+
+    private fun asyncHandlerForCurrentThreadOrMainIfNoLooper(): Handler {
+        val looper = Looper.myLooper() ?: Looper.getMainLooper()
+        return HandlerHelper.createAsync(looper)
+    }
+}
+
+/**
+ * To allow mocking for tests
+ */
+internal interface FontsContractCompatLoader {
+    fun requestFont(
+        context: Context,
+        fontRequest: FontRequest,
+        typefaceStyle: Int,
+        handler: Handler,
+        callback: FontsContractCompat.FontRequestCallback
+    )
+}
+
+/**
+ * Actual implementation of requestFont using androidx.core
+ */
+private object DefaultFontsContractCompatLoader : FontsContractCompatLoader {
+    override fun requestFont(
+        context: Context,
+        fontRequest: FontRequest,
+        typefaceStyle: Int,
+        handler: Handler,
+        callback: FontsContractCompat.FontRequestCallback
+    ) {
+        FontsContractCompat.requestFont(
+            context,
+            fontRequest,
+            typefaceStyle,
+            false, /* isBlockingFetch*/
+            0, /* timeout - not used when isBlockingFetch=false */
+            handler,
+            callback
+        )
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/HandlerHelper.kt b/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/HandlerHelper.kt
new file mode 100644
index 0000000..782d2d4
--- /dev/null
+++ b/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/HandlerHelper.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.googlefonts
+
+import android.os.Build
+import android.os.Handler
+import android.os.Looper
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+
+/**
+ * Handler Helper helps make handlers.
+ *
+ * (with Async support API28+)
+ */
+internal object HandlerHelper {
+
+    /**
+     * @return handler, with createAsync if API level supports it.
+     */
+    fun createAsync(looper: Looper): Handler {
+        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            Handler28Impl.createAsync(looper)
+        } else {
+            Handler(looper)
+        }
+    }
+
+    @RequiresApi(28)
+    internal object Handler28Impl {
+        @DoNotInline
+        fun createAsync(looper: Looper): Handler {
+            return Handler.createAsync(looper)
+        }
+    }
+}
diff --git a/compose/ui/ui-text/api/current.ignore b/compose/ui/ui-text/api/current.ignore
index 360cd29..b062517 100644
--- a/compose/ui/ui-text/api/current.ignore
+++ b/compose/ui/ui-text/api/current.ignore
@@ -1,3 +1,7 @@
 // Baseline format: 1.0
+InvalidNullConversion: androidx.compose.ui.text.font.FontKt#Font(int, androidx.compose.ui.text.font.FontWeight, int):
+    Attempted to remove @NonNull annotation from method androidx.compose.ui.text.font.FontKt.Font(int,androidx.compose.ui.text.font.FontWeight,int)
+
+
 RemovedClass: androidx.compose.ui.text.TextLayoutResultKt:
     Removed class androidx.compose.ui.text.TextLayoutResultKt
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index efd3293..63de602 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -616,7 +616,8 @@
   }
 
   public final class FontKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font! Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.FontFamily toFontFamily(androidx.compose.ui.text.font.Font);
   }
 
@@ -628,6 +629,21 @@
   public final class FontListFontFamilyTypefaceAdapterKt {
   }
 
+  @kotlin.jvm.JvmInline public final value class FontLoadingStrategy {
+    method public int getValue();
+    property public final int value;
+    field public static final androidx.compose.ui.text.font.FontLoadingStrategy.Companion Companion;
+  }
+
+  public static final class FontLoadingStrategy.Companion {
+    method public int getAsync();
+    method public int getBlocking();
+    method public int getOptionalLocal();
+    property public final int Async;
+    property public final int Blocking;
+    property public final int OptionalLocal;
+  }
+
   public final inline class FontStyle {
     ctor public FontStyle();
     method public int getValue();
diff --git a/compose/ui/ui-text/api/public_plus_experimental_current.txt b/compose/ui/ui-text/api/public_plus_experimental_current.txt
index bd2a8d8..c2c70dc 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_current.txt
@@ -635,8 +635,8 @@
   }
 
   public final class FontKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
-    method @androidx.compose.runtime.Stable @androidx.compose.ui.text.ExperimentalTextApi public static androidx.compose.ui.text.font.Font Font(int resId, int loadingStrategy, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font! Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.FontFamily toFontFamily(androidx.compose.ui.text.font.Font);
   }
 
@@ -648,7 +648,7 @@
   public final class FontListFontFamilyTypefaceAdapterKt {
   }
 
-  @androidx.compose.ui.text.ExperimentalTextApi @kotlin.jvm.JvmInline public final value class FontLoadingStrategy {
+  @kotlin.jvm.JvmInline public final value class FontLoadingStrategy {
     method public int getValue();
     property public final int value;
     field public static final androidx.compose.ui.text.font.FontLoadingStrategy.Companion Companion;
diff --git a/compose/ui/ui-text/api/restricted_current.ignore b/compose/ui/ui-text/api/restricted_current.ignore
index 360cd29..b062517 100644
--- a/compose/ui/ui-text/api/restricted_current.ignore
+++ b/compose/ui/ui-text/api/restricted_current.ignore
@@ -1,3 +1,7 @@
 // Baseline format: 1.0
+InvalidNullConversion: androidx.compose.ui.text.font.FontKt#Font(int, androidx.compose.ui.text.font.FontWeight, int):
+    Attempted to remove @NonNull annotation from method androidx.compose.ui.text.font.FontKt.Font(int,androidx.compose.ui.text.font.FontWeight,int)
+
+
 RemovedClass: androidx.compose.ui.text.TextLayoutResultKt:
     Removed class androidx.compose.ui.text.TextLayoutResultKt
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index efd3293..63de602 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -616,7 +616,8 @@
   }
 
   public final class FontKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy);
+    method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font! Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.FontFamily toFontFamily(androidx.compose.ui.text.font.Font);
   }
 
@@ -628,6 +629,21 @@
   public final class FontListFontFamilyTypefaceAdapterKt {
   }
 
+  @kotlin.jvm.JvmInline public final value class FontLoadingStrategy {
+    method public int getValue();
+    property public final int value;
+    field public static final androidx.compose.ui.text.font.FontLoadingStrategy.Companion Companion;
+  }
+
+  public static final class FontLoadingStrategy.Companion {
+    method public int getAsync();
+    method public int getBlocking();
+    method public int getOptionalLocal();
+    property public final int Async;
+    property public final int Blocking;
+    property public final int OptionalLocal;
+  }
+
   public final inline class FontStyle {
     ctor public FontStyle();
     method public int getValue();
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/Font.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/Font.kt
index 6aea6a3..5aa7e6f 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/Font.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/Font.kt
@@ -232,10 +232,6 @@
  * frame they are used until the font is loaded. This is the correct behavior for small fonts
  * available locally.
  *
- * To load fonts from a remote resource, it is recommended to call
- * [Font](Int, FontLoadingStrategy, ...) and provide [FontLoadingStrategy.Async] as the second
- * parameter.
- *
  * @param resId The resource ID of the font file in font resources. i.e. "R.font.myfont".
  * @param weight The weight of the font. The system uses this to match a font to a font request
  * that is given in a [androidx.compose.ui.text.SpanStyle].
@@ -247,10 +243,14 @@
  *
  * @see FontFamily
  */
-// TODO: When FontLoadingStrategy is stable, deprecate HIDDEN this for binary compatible overload
-//  and add a default parameter to other overload for source compatible (deferred because adding an
-//  optional experimental parameters makes call sites experimental).
-@OptIn(ExperimentalTextApi::class)
+// TODO(b/219783755): Remove this when safe after Compose 1.3
+@Deprecated(
+    "Maintained for binary compatibility until Compose 1.3.",
+    replaceWith = ReplaceWith(
+        "Font(resId, weight, style)"
+    ),
+    DeprecationLevel.HIDDEN
+)
 @Stable
 fun Font(
     resId: Int,
@@ -270,25 +270,20 @@
  * [FontLoadingStrategy.Async].
  *
  * @param resId The resource ID of the font file in font resources. i.e. "R.font.myfont".
- * @param loadingStrategy Load strategy for this font, may be async for async resource fonts
  * @param weight The weight of the font. The system uses this to match a font to a font request
  * that is given in a [androidx.compose.ui.text.SpanStyle].
  * @param style The style of the font, normal or italic. The system uses this to match a font to a
  * font request that is given in a [androidx.compose.ui.text.SpanStyle].
+ * @param loadingStrategy Load strategy for this font, may be async for async resource fonts
  *
  * @see FontFamily
  */
-// TODO: When FontLoadingStrategy is stable, move fontLoad parameter to last position, add default,
-//  and promote  to stable API in the same release. This maintains source compatibility with the
-//  original  overload's positional ordering as well as adding a default param (deferred because new
-//  default parameters of experimental type mark call site as experimental)
-@ExperimentalTextApi
 @Stable
 fun Font(
     resId: Int,
-    loadingStrategy: FontLoadingStrategy,
     weight: FontWeight = FontWeight.Normal,
-    style: FontStyle = FontStyle.Normal
+    style: FontStyle = FontStyle.Normal,
+    loadingStrategy: FontLoadingStrategy = FontLoadingStrategy.Blocking
 ): Font = ResourceFont(resId, weight, style, loadingStrategy)
 
 /**
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontLoadingStrategy.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontLoadingStrategy.kt
index f41a259..ad4c017 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontLoadingStrategy.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontLoadingStrategy.kt
@@ -16,8 +16,6 @@
 
 package androidx.compose.ui.text.font
 
-import androidx.compose.ui.text.ExperimentalTextApi
-
 /**
  * Font loading strategy for a [Font] in a [FontListFontFamily].
  *
@@ -25,8 +23,6 @@
  *
  * For more information about font family resolution see [FontFamily].
  */
-// TODO: When making stable, resolve Font(resId) != Font(resId, fontLoad) in Font.kt
-@ExperimentalTextApi
 @kotlin.jvm.JvmInline
 value class FontLoadingStrategy private constructor(val value: Int) {
     override fun toString(): String {
diff --git a/core/OWNERS b/core/OWNERS
index 6a8e25d..d39071c 100644
--- a/core/OWNERS
+++ b/core/OWNERS
@@ -17,3 +17,6 @@
 # For shortcut related files
 [email protected]
 [email protected]
+
+# Per-file for platform ownership
+per-file src/main/java/androidx/core/content/FileProvider.java = [email protected]
diff --git a/core/core-animation-integration-tests/testapp/src/androidTest/java/androidx/core/animation/ObjectAnimatorTest.java b/core/core-animation-integration-tests/testapp/src/androidTest/java/androidx/core/animation/ObjectAnimatorTest.java
index 82dba71..f85edaa 100644
--- a/core/core-animation-integration-tests/testapp/src/androidTest/java/androidx/core/animation/ObjectAnimatorTest.java
+++ b/core/core-animation-integration-tests/testapp/src/androidTest/java/androidx/core/animation/ObjectAnimatorTest.java
@@ -204,7 +204,7 @@
         int startColor = 0xFFFF8080;
         int endColor = 0xFF8080FF;
 
-        Integer[] values = {new Integer(startColor), new Integer(endColor)};
+        Integer[] values = {startColor, endColor};
         ArgbEvaluator evaluator = ArgbEvaluator.getInstance();
         final ObjectAnimator colorAnimator = ObjectAnimator.ofObject(object, property,
                 evaluator, values);
diff --git a/core/core-appdigest/src/androidTest/java/androidx/core/appdigest/ChecksumsTest.java b/core/core-appdigest/src/androidTest/java/androidx/core/appdigest/ChecksumsTest.java
index f01b379..90b69bc 100644
--- a/core/core-appdigest/src/androidTest/java/androidx/core/appdigest/ChecksumsTest.java
+++ b/core/core-appdigest/src/androidTest/java/androidx/core/appdigest/ChecksumsTest.java
@@ -631,6 +631,7 @@
     @SdkSuppress(minSdkVersion = 29)
     @LargeTest
     @Test
+    @SuppressWarnings("deprecation")
     public void testFixedAllFileChecksumsDirectExecutor() throws Exception {
         installPackage(TEST_FIXED_APK);
         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
diff --git a/core/core-appdigest/src/main/java/androidx/core/appdigest/Checksums.java b/core/core-appdigest/src/main/java/androidx/core/appdigest/Checksums.java
index 750ac3d..dbb0098 100644
--- a/core/core-appdigest/src/main/java/androidx/core/appdigest/Checksums.java
+++ b/core/core-appdigest/src/main/java/androidx/core/appdigest/Checksums.java
@@ -106,6 +106,7 @@
      * @throws PackageManager.NameNotFoundException if a package with the given name cannot be
      *                                              found on the system.
      */
+    @SuppressWarnings("deprecation")
     public static @NonNull ListenableFuture<Checksum[]> getChecksums(@NonNull Context context,
             @NonNull String packageName, boolean includeSplits, final @Checksum.Type int required,
             @NonNull List<Certificate> trustedInstallers, @NonNull Executor executor)
diff --git a/core/core-appdigest/src/main/java/androidx/core/appdigest/ChecksumsApiSImpl.java b/core/core-appdigest/src/main/java/androidx/core/appdigest/ChecksumsApiSImpl.java
index 6916edb..d883448 100644
--- a/core/core-appdigest/src/main/java/androidx/core/appdigest/ChecksumsApiSImpl.java
+++ b/core/core-appdigest/src/main/java/androidx/core/appdigest/ChecksumsApiSImpl.java
@@ -134,6 +134,7 @@
     }
 
     @SuppressLint("WrongConstant")
+    @SuppressWarnings("deprecation")
     static void getInstallerChecksums(@NonNull Context context,
             String split, File file,
             @Checksum.Type int required,
diff --git a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/TrampolineActivityTest.java b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/TrampolineActivityTest.java
index b14761a..fd7b0b4 100644
--- a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/TrampolineActivityTest.java
+++ b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/TrampolineActivityTest.java
@@ -126,6 +126,7 @@
 
     @Test
     @SmallTest
+    @SuppressWarnings("deprecation")
     public void testManifest_canDiscoverMetadata() {
         PackageManager packageManager = mContext.getPackageManager();
         Intent activityIntent = new Intent(SHORTCUT_LISTENER_INTENT_FILTER_ACTION);
diff --git a/core/core-remoteviews/src/main/AndroidManifest.xml b/core/core-remoteviews/src/main/AndroidManifest.xml
index 9a694b6..9435eb8 100644
--- a/core/core-remoteviews/src/main/AndroidManifest.xml
+++ b/core/core-remoteviews/src/main/AndroidManifest.xml
@@ -14,9 +14,16 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<manifest package="androidx.core.remoteviews" xmlns:android="http://schemas.android.com/apk/res/android">
+<manifest
+    package="androidx.core.remoteviews"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
     <application>
-      <service android:name="androidx.core.widget.RemoteViewsCompatService"
-            android:permission="android.permission.BIND_REMOTEVIEWS"/>
+      <service
+          android:name="androidx.core.widget.RemoteViewsCompatService"
+          android:permission="android.permission.BIND_REMOTEVIEWS"
+          tools:ignore="MissingServiceExportedEqualsTrue" />
     </application>
+
 </manifest>
diff --git a/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompat.kt b/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompat.kt
index ab0550c..8fa7f2f 100644
--- a/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompat.kt
+++ b/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompat.kt
@@ -64,6 +64,7 @@
      * @param items       The items to display in the [android.widget.AdapterView].
      */
     @JvmStatic
+    @Suppress("DEPRECATION")
     public fun setRemoteAdapter(
         context: Context,
         remoteViews: RemoteViews,
@@ -3933,4 +3934,4 @@
             rv.setFloatDimenAttr(id, method, resId)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompatService.kt b/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompatService.kt
index 46b257b..34a449d 100644
--- a/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompatService.kt
+++ b/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompatService.kt
@@ -222,6 +222,7 @@
                 }
             }
 
+            @Suppress("DEPRECATION")
             internal fun getVersionCode(context: Context): Long? {
                 val packageManager = context.packageManager
                 val packageInfo = try {
@@ -306,4 +307,4 @@
             RemoteViewsCompatServiceData.create(context, items).save(context, appWidgetId, viewId)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/core/lint-baseline.xml b/core/core/lint-baseline.xml
index 0864e15..26689c9 100644
--- a/core/core/lint-baseline.xml
+++ b/core/core/lint-baseline.xml
@@ -1048,6 +1048,17 @@
 
     <issue
         id="WrongConstant"
+        message="Must be one or more of: AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_TEXT, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED"
+        errorLine1="            return event.getContentChangeTypes();"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
+            line="344"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="WrongConstant"
         message="Must be one of: Action.SEMANTIC_ACTION_NONE, Action.SEMANTIC_ACTION_REPLY, Action.SEMANTIC_ACTION_MARK_AS_READ, Action.SEMANTIC_ACTION_MARK_AS_UNREAD, Action.SEMANTIC_ACTION_DELETE, Action.SEMANTIC_ACTION_ARCHIVE, Action.SEMANTIC_ACTION_MUTE, Action.SEMANTIC_ACTION_UNMUTE, Action.SEMANTIC_ACTION_THUMBS_UP, Action.SEMANTIC_ACTION_THUMBS_DOWN, Action.SEMANTIC_ACTION_CALL"
         errorLine1="                    builder.setSemanticAction(action.getSemanticAction());"
         errorLine2="                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/emoji2/emoji2/build.gradle b/emoji2/emoji2/build.gradle
index 13daaf4..9c5c245 100644
--- a/emoji2/emoji2/build.gradle
+++ b/emoji2/emoji2/build.gradle
@@ -24,7 +24,7 @@
     api("androidx.startup:startup-runtime:1.0.0")
     implementation("androidx.collection:collection:1.1.0")
     implementation("androidx.annotation:annotation:1.2.0")
-    implementation("androidx.lifecycle:lifecycle-process:2.4.0-rc01")
+    implementation("androidx.lifecycle:lifecycle-process:2.4.1")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
diff --git a/emoji2/emoji2/src/androidTest/java/androidx/emoji2/text/DefaultEmojiCompatConfigTest.java b/emoji2/emoji2/src/androidTest/java/androidx/emoji2/text/DefaultEmojiCompatConfigTest.java
index 79cb95c..8d936cb 100644
--- a/emoji2/emoji2/src/androidTest/java/androidx/emoji2/text/DefaultEmojiCompatConfigTest.java
+++ b/emoji2/emoji2/src/androidTest/java/androidx/emoji2/text/DefaultEmojiCompatConfigTest.java
@@ -71,6 +71,7 @@
         }
     }
 
+    @SuppressWarnings("deprecation")
     private boolean providerOnSystem() {
         if (Build.VERSION.SDK_INT < 19) {
             return false;
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/DefaultEmojiCompatConfig.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/DefaultEmojiCompatConfig.java
index dbb4ea4..c874512 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/DefaultEmojiCompatConfig.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/DefaultEmojiCompatConfig.java
@@ -303,6 +303,7 @@
             extends DefaultEmojiCompatConfigHelper {
         @NonNull
         @Override
+        @SuppressWarnings("deprecation")
         public List<ResolveInfo> queryIntentContentProviders(@NonNull PackageManager packageManager,
                 @NonNull Intent intent, int flags) {
             return packageManager.queryIntentContentProviders(intent, flags);
diff --git a/enterprise/enterprise-feedback/src/main/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporter.java b/enterprise/enterprise-feedback/src/main/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporter.java
index 9f71a7d..c6b4aa4 100644
--- a/enterprise/enterprise-feedback/src/main/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporter.java
+++ b/enterprise/enterprise-feedback/src/main/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporter.java
@@ -220,6 +220,7 @@
         return false;
     }
 
+    @SuppressWarnings("deprecation")
     private Collection<ServiceInfo> getServiceInfoInPackages(
             Intent intent, Collection<String> acceptablePackageNames) {
         PackageManager packageManager = mContext.getPackageManager();
diff --git a/fragment/fragment-ktx/api/api_lint.ignore b/fragment/fragment-ktx/api/api_lint.ignore
index a8ac758..e42cfe7 100644
--- a/fragment/fragment-ktx/api/api_lint.ignore
+++ b/fragment/fragment-ktx/api/api_lint.ignore
@@ -5,7 +5,7 @@
     Missing nullability on method `add` return
 MissingNullability: androidx.fragment.app.FragmentTransactionKt#replace(androidx.fragment.app.FragmentTransaction, int, String, android.os.Bundle):
     Missing nullability on method `replace` return
-MissingNullability: androidx.fragment.app.FragmentViewModelLazyKt#activityViewModels(androidx.fragment.app.Fragment, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
+MissingNullability: androidx.fragment.app.FragmentViewModelLazyKt#activityViewModels(androidx.fragment.app.Fragment, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
     Missing nullability on method `activityViewModels` return
-MissingNullability: androidx.fragment.app.FragmentViewModelLazyKt#viewModels(androidx.fragment.app.Fragment, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
+MissingNullability: androidx.fragment.app.FragmentViewModelLazyKt#viewModels(androidx.fragment.app.Fragment, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
     Missing nullability on method `viewModels` return
diff --git a/fragment/fragment-ktx/api/current.ignore b/fragment/fragment-ktx/api/current.ignore
new file mode 100644
index 0000000..a75bd4a
--- /dev/null
+++ b/fragment/fragment-ktx/api/current.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+ChangedType: androidx.fragment.app.FragmentViewModelLazyKt#createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
+    Method androidx.fragment.app.FragmentViewModelLazyKt.createViewModelLazy has changed return type from kotlin.Lazy<VM> to kotlin.Lazy<? extends VM>
+
+
+InvalidNullConversion: androidx.fragment.app.FragmentViewModelLazyKt#createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
+    Attempted to remove @NonNull annotation from method androidx.fragment.app.FragmentViewModelLazyKt.createViewModelLazy(androidx.fragment.app.Fragment,kotlin.reflect.KClass<VM>,kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore>,kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>)
diff --git a/fragment/fragment-ktx/api/current.txt b/fragment/fragment-ktx/api/current.txt
index 946af54..6d0a8d3 100644
--- a/fragment/fragment-ktx/api/current.txt
+++ b/fragment/fragment-ktx/api/current.txt
@@ -21,10 +21,12 @@
   }
 
   public final class FragmentViewModelLazyKt {
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer);
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
   }
 
   public final class ViewKt {
diff --git a/fragment/fragment-ktx/api/public_plus_experimental_current.txt b/fragment/fragment-ktx/api/public_plus_experimental_current.txt
index 946af54..6d0a8d3 100644
--- a/fragment/fragment-ktx/api/public_plus_experimental_current.txt
+++ b/fragment/fragment-ktx/api/public_plus_experimental_current.txt
@@ -21,10 +21,12 @@
   }
 
   public final class FragmentViewModelLazyKt {
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer);
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
   }
 
   public final class ViewKt {
diff --git a/fragment/fragment-ktx/api/restricted_current.ignore b/fragment/fragment-ktx/api/restricted_current.ignore
new file mode 100644
index 0000000..a75bd4a
--- /dev/null
+++ b/fragment/fragment-ktx/api/restricted_current.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+ChangedType: androidx.fragment.app.FragmentViewModelLazyKt#createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
+    Method androidx.fragment.app.FragmentViewModelLazyKt.createViewModelLazy has changed return type from kotlin.Lazy<VM> to kotlin.Lazy<? extends VM>
+
+
+InvalidNullConversion: androidx.fragment.app.FragmentViewModelLazyKt#createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
+    Attempted to remove @NonNull annotation from method androidx.fragment.app.FragmentViewModelLazyKt.createViewModelLazy(androidx.fragment.app.Fragment,kotlin.reflect.KClass<VM>,kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore>,kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>)
diff --git a/fragment/fragment-ktx/api/restricted_current.txt b/fragment/fragment-ktx/api/restricted_current.txt
index 946af54..6d0a8d3 100644
--- a/fragment/fragment-ktx/api/restricted_current.txt
+++ b/fragment/fragment-ktx/api/restricted_current.txt
@@ -21,10 +21,12 @@
   }
 
   public final class FragmentViewModelLazyKt {
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer);
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
   }
 
   public final class ViewKt {
diff --git a/fragment/fragment-ktx/src/androidTest/java/androidx/fragment/app/FragmentViewModelLazyTest.kt b/fragment/fragment-ktx/src/androidTest/java/androidx/fragment/app/FragmentViewModelLazyTest.kt
index 295e15cd..5ab1dda 100644
--- a/fragment/fragment-ktx/src/androidTest/java/androidx/fragment/app/FragmentViewModelLazyTest.kt
+++ b/fragment/fragment-ktx/src/androidTest/java/androidx/fragment/app/FragmentViewModelLazyTest.kt
@@ -52,6 +52,8 @@
         assertThat(fragment.activityVM).isEqualTo(activityRule.activity.vm)
         assertThat(fragment.activityVM2).isEqualTo(activityRule.activity.vm2)
         assertThat(fragment.savedStateViewModel.defaultValue).isEqualTo("value")
+        assertThat(fragment.activityVMCE.defaultValue).isEqualTo("value2")
+        assertThat(fragment.savedStateViewModelCE.defaultValue).isEqualTo("value3")
     }
 
     class TestVMFragment : Fragment() {
@@ -62,6 +64,26 @@
         val activityVM: TestActivityViewModel by activityViewModels()
         val activityVM2: TestActivityViewModel2 by viewModels({ requireActivity() })
         val savedStateViewModel: TestSavedStateViewModel by viewModels({ requireActivity() })
+        val activityVMCE: TestActivityViewModelCE by activityViewModels(
+            extrasProducer = {
+                MutableCreationExtras().apply {
+                    set(SAVED_STATE_REGISTRY_OWNER_KEY, requireActivity())
+                    set(VIEW_MODEL_STORE_OWNER_KEY, requireActivity())
+                    set(DEFAULT_ARGS_KEY, bundleOf("test" to "value2"))
+                }
+            }
+        )
+        val savedStateViewModelCE: TestSavedStateViewModelCE by viewModels(
+            ownerProducer = { requireActivity() },
+            extrasProducer = {
+                MutableCreationExtras().apply {
+                    set(SAVED_STATE_REGISTRY_OWNER_KEY, requireActivity())
+                    set(VIEW_MODEL_STORE_OWNER_KEY, requireActivity())
+                    set(DEFAULT_ARGS_KEY, bundleOf("test" to "value3"))
+                }
+            }
+        )
+
         override fun onCreate(savedInstanceState: Bundle?) {
             injectedFactory = VMFactory("dagger")
             super.onCreate(savedInstanceState)
@@ -99,6 +121,12 @@
     class TestSavedStateViewModel(handle: SavedStateHandle) : ViewModel() {
         val defaultValue = handle.get<String>("test")
     }
+    class TestActivityViewModelCE(handle: SavedStateHandle) : ViewModel() {
+        val defaultValue = handle.get<String>("test")
+    }
+    class TestSavedStateViewModelCE(handle: SavedStateHandle) : ViewModel() {
+        val defaultValue = handle.get<String>("test")
+    }
 
     private class VMFactory(val prop: String) : ViewModelProvider.Factory {
         @Suppress("UNCHECKED_CAST")
diff --git a/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt b/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt
index e281eef..12b7299 100644
--- a/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt
+++ b/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt
@@ -53,6 +53,10 @@
  * This property can be accessed only after this Fragment is attached i.e., after
  * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
  */
+@Deprecated(
+    "Superseded by viewModels that takes a CreationExtras producer",
+    level = DeprecationLevel.HIDDEN
+)
 @MainThread
 public inline fun <reified VM : ViewModel> Fragment.viewModels(
     noinline ownerProducer: () -> ViewModelStoreOwner = { this },
@@ -62,16 +66,93 @@
     return createViewModelLazy(
         VM::class,
         { owner.viewModelStore },
+        {
+            (owner as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras
+                ?: CreationExtras.Empty
+        },
         factoryProducer ?: {
             (owner as? HasDefaultViewModelProviderFactory)?.defaultViewModelProviderFactory
                 ?: defaultViewModelProviderFactory
-        }) {
-        (owner as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras
-            ?: CreationExtras.Empty
-    }
+        })
 }
 
 /**
+ * Returns a property delegate to access [ViewModel] by **default** scoped to this [Fragment]:
+ * ```
+ * class MyFragment : Fragment() {
+ *     val viewmodel: MyViewModel by viewmodels()
+ * }
+ * ```
+ *
+ * Custom [ViewModelProvider.Factory] can be defined via [factoryProducer] parameter,
+ * factory returned by it will be used to create [ViewModel]:
+ * ```
+ * class MyFragment : Fragment() {
+ *     val viewmodel: MyViewModel by viewmodels { myFactory }
+ * }
+ * ```
+ *
+ * Default scope may be overridden with parameter [ownerProducer]:
+ * ```
+ * class MyFragment : Fragment() {
+ *     val viewmodel: MyViewModel by viewmodels ({requireParentFragment()})
+ * }
+ * ```
+ *
+ * This property can be accessed only after this Fragment is attached i.e., after
+ * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
+ */
+@MainThread
+public inline fun <reified VM : ViewModel> Fragment.viewModels(
+    noinline ownerProducer: () -> ViewModelStoreOwner = { this },
+    noinline extrasProducer: (() -> CreationExtras)? = null,
+    noinline factoryProducer: (() -> Factory)? = null
+): Lazy<VM> {
+    val owner by lazy(LazyThreadSafetyMode.NONE) { ownerProducer() }
+    return createViewModelLazy(
+        VM::class,
+        { owner.viewModelStore },
+        {
+            extrasProducer?.invoke()
+            ?: (owner as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras
+            ?: CreationExtras.Empty
+        },
+        factoryProducer ?: {
+            (owner as? HasDefaultViewModelProviderFactory)?.defaultViewModelProviderFactory
+                ?: defaultViewModelProviderFactory
+        })
+}
+
+/**
+ * Returns a property delegate to access parent activity's [ViewModel],
+ * if [factoryProducer] is specified then [ViewModelProvider.Factory]
+ * returned by it will be used to create [ViewModel] first time. Otherwise, the activity's
+ * [androidx.activity.ComponentActivity.getDefaultViewModelProviderFactory](default factory)
+ * will be used.
+ *
+ * ```
+ * class MyFragment : Fragment() {
+ *     val viewmodel: MyViewModel by activityViewModels()
+ * }
+ * ```
+ *
+ * This property can be accessed only after this Fragment is attached i.e., after
+ * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
+ */
+@Deprecated(
+    "Superseded by activityViewModels that takes a CreationExtras producer",
+    level = DeprecationLevel.HIDDEN
+)
+@MainThread
+public inline fun <reified VM : ViewModel> Fragment.activityViewModels(
+    noinline factoryProducer: (() -> Factory)? = null
+): Lazy<VM> = createViewModelLazy(
+    VM::class, { requireActivity().viewModelStore },
+    { requireActivity().defaultViewModelCreationExtras },
+    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory }
+)
+
+/**
  * Returns a property delegate to access parent activity's [ViewModel],
  * if [factoryProducer] is specified then [ViewModelProvider.Factory]
  * returned by it will be used to create [ViewModel] first time. Otherwise, the activity's
@@ -89,17 +170,23 @@
  */
 @MainThread
 public inline fun <reified VM : ViewModel> Fragment.activityViewModels(
+    noinline extrasProducer: (() -> CreationExtras)? = null,
     noinline factoryProducer: (() -> Factory)? = null
 ): Lazy<VM> = createViewModelLazy(
     VM::class, { requireActivity().viewModelStore },
-    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory },
-    { requireActivity().defaultViewModelCreationExtras }
+    { extrasProducer?.invoke() ?: requireActivity().defaultViewModelCreationExtras },
+    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory }
+
 )
 
 /**
  * Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
  * to default factory.
  */
+@Deprecated(
+    "Superseded by createViewModelLazy that takes a CreationExtras producer",
+    level = DeprecationLevel.HIDDEN
+)
 @MainThread
 public fun <VM : ViewModel> Fragment.createViewModelLazy(
     viewModelClass: KClass<VM>,
@@ -108,8 +195,9 @@
 ): Lazy<VM> = createViewModelLazy(
     viewModelClass,
     storeProducer,
+    { defaultViewModelCreationExtras },
     factoryProducer
-) { defaultViewModelCreationExtras }
+)
 
 /**
  * Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
@@ -122,8 +210,9 @@
 public fun <VM : ViewModel> Fragment.createViewModelLazy(
     viewModelClass: KClass<VM>,
     storeProducer: () -> ViewModelStore,
-    factoryProducer: (() -> Factory)? = null,
-    extrasProducer: () -> CreationExtras = { defaultViewModelCreationExtras }
+    extrasProducer: () -> CreationExtras = { defaultViewModelCreationExtras },
+    factoryProducer: (() -> Factory)? = null
+
 ): Lazy<VM> {
     val factoryPromise = factoryProducer ?: {
         defaultViewModelProviderFactory
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml b/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml
index 6e0b227..4664d0c 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml
@@ -13,8 +13,10 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.glance.appwidget.demos">
+<manifest
+    package="androidx.glance.appwidget.demos"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
 
     <application
         android:allowBackup="false"
@@ -34,7 +36,9 @@
         </activity>
 
         <activity android:name=".ActionDemoActivity" />
-        <service android:name=".ActionDemoService" />
+        <service
+            android:name=".ActionDemoService"
+            tools:ignore="MissingServiceExportedEqualsTrue" />
 
         <activity android:name=".ListClickDestinationActivity" android:exported="false" />
 
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/SingleEntityWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/SingleEntityWidget.kt
index d36cc28..043e781 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/SingleEntityWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/SingleEntityWidget.kt
@@ -31,9 +31,10 @@
 import androidx.template.appwidget.GlanceTemplateAppWidget
 import androidx.template.template.TemplateImageWithDescription
 import androidx.template.template.SingleEntityTemplate
+import androidx.template.template.TemplateText
 import androidx.template.template.TemplateTextButton
 
-/** A [SingleEntityTemplate] implementation that sets the header given widget state */
+/** A [SingleEntityTemplate] implementation that sets the title given widget state */
 class MyWidgetTemplate : SingleEntityTemplate() {
     override fun getData(state: Any?): Data {
         require(state is Preferences)
@@ -59,16 +60,16 @@
     override val glanceAppWidget: GlanceAppWidget = SingleEntityWidget()
 }
 
-private fun createData(header: String) = SingleEntityTemplate.Data(
-    header = header,
+private fun createData(title: String) = SingleEntityTemplate.Data(
+    header = TemplateText("Single Entity demo"),
     headerIcon = TemplateImageWithDescription(
         ImageProvider(R.drawable.compose),
         "Header icon"
     ),
-    title = "",
-    bodyText = "",
+    title = TemplateText(title, 1),
+    subtitle = TemplateText("Subtitle test", 2),
     button = TemplateTextButton(actionRunCallback<ButtonAction>(), "toggle"),
-    mainImage = TemplateImageWithDescription(
+    image = TemplateImageWithDescription(
         ImageProvider(R.drawable.compose),
         "Compose image"
     ),
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 870260b..7552c20 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -438,62 +438,6 @@
             <sha256 value="02c12c3c2ae12dd475219ff691c82a4d9ea21f44bc594a181295bf6d43dcfbb0" origin="Generated by Gradle"/>
          </artifact>
       </component>
-      <component group="com.google.devtools.ksp" name="symbol-processing" version="1.5.30-1.0.0">
-         <artifact name="symbol-processing-1.5.30-1.0.0.jar">
-            <sha256 value="abb95c72ad7a76eca777fcc94c83d1f907b5d70895f6571a1a9313d3ecb05588" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="symbol-processing-1.5.30-1.0.0.pom">
-            <sha256 value="91423e540336a6cefa4bbbf772da55bc477f4e0b72e547c66a81cf4f5acabbcb" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="com.google.devtools.ksp" name="symbol-processing" version="1.5.31-1.0.0">
-         <artifact name="symbol-processing-1.5.31-1.0.0.jar">
-            <sha256 value="0a9928423e04bcd642623d4c997232f6f60c7e4523f01d5963fae66bddd004fd" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="symbol-processing-1.5.31-1.0.0.pom">
-            <sha256 value="bd9c11c632b591f8159c6b8b958040bbdbb4895ae6799d38227b0769fbf6e09a" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="com.google.devtools.ksp" name="symbol-processing-api" version="1.5.20-1.0.0-beta03">
-         <artifact name="symbol-processing-api-1.5.20-1.0.0-beta03.jar">
-            <sha256 value="4812505df0a6e08bf8c167d358b31abf96c8a3784e6d4074587919955f2df4bd" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="symbol-processing-api-1.5.20-1.0.0-beta03.module">
-            <sha256 value="2e2e1673a07dc04a39a3161cddd5cf860b125a507ca20c2f89db6252f59c6487" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="com.google.devtools.ksp" name="symbol-processing-api" version="1.5.30-1.0.0">
-         <artifact name="symbol-processing-api-1.5.30-1.0.0.jar">
-            <sha256 value="bf4a6875af46917174b087d03840456685e115954d926ce88fd04b9d2f254df3" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="symbol-processing-api-1.5.30-1.0.0.module">
-            <sha256 value="c82a98246729a2186e6c3b431a4d04bf957612707e11fadc392f23e4d85328ff" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="com.google.devtools.ksp" name="symbol-processing-api" version="1.5.31-1.0.0">
-         <artifact name="symbol-processing-api-1.5.31-1.0.0.jar">
-            <sha256 value="2dc570ace6f7452e7196c090a3f86df7b6c5fa9755bd0212a687ae060cf523f6" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="symbol-processing-api-1.5.31-1.0.0.module">
-            <sha256 value="9b200e651b4b6dd21553ea857e3b4ad2fbdf1a6b724127189c16ef5fa8e5fed2" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="com.google.devtools.ksp" name="symbol-processing-gradle-plugin" version="1.5.30-1.0.0">
-         <artifact name="symbol-processing-gradle-plugin-1.5.30-1.0.0.jar">
-            <sha256 value="e829abac4f87293784aed54614335386e980c2c0871c0c4d48caf4e2c36321f0" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="symbol-processing-gradle-plugin-1.5.30-1.0.0.module">
-            <sha256 value="d67e2f8b403d24a13b594c50e64ca248f38650f9e7dfb6d7c97813593141e92e" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
-      <component group="com.google.devtools.ksp" name="symbol-processing-gradle-plugin" version="1.5.31-1.0.0">
-         <artifact name="symbol-processing-gradle-plugin-1.5.31-1.0.0.jar">
-            <sha256 value="42e755966bc5abaf54b247f9648e682d7311d4568d9268e67dd9ba44afbb92e5" origin="Generated by Gradle"/>
-         </artifact>
-         <artifact name="symbol-processing-gradle-plugin-1.5.31-1.0.0.module">
-            <sha256 value="30f37be681a129b1feb0a83d46ef4f3e48e015c4bf2c9927234f549d191059bd" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
       <component group="com.google.firebase" name="firebase-appindexing" version="20.0.0">
          <artifact name="firebase-appindexing-20.0.0.aar">
             <sha256 value="c07aee785d8e0644f38895955f6d4e8808bb43f44c5660b05ff4b828700ac96c" origin="Generated by Gradle"/>
diff --git a/health/health-data-client/api/current.txt b/health/health-data-client/api/current.txt
index e6f50d0..50a64a6a 100644
--- a/health/health-data-client/api/current.txt
+++ b/health/health-data-client/api/current.txt
@@ -1 +1,31 @@
 // Signature format: 4.0
+package androidx.health.data.client.metadata {
+
+  public final class Device {
+    ctor public Device(optional String? identifier, optional String? manufacturer, optional String? model, optional String? type);
+    method public String? getIdentifier();
+    method public String? getManufacturer();
+    method public String? getModel();
+    method public String? getType();
+    property public final String? identifier;
+    property public final String? manufacturer;
+    property public final String? model;
+    property public final String? type;
+  }
+
+  public final class Metadata {
+    ctor public Metadata(optional String? uid, optional java.time.Instant lastModifiedTime, optional String? clientId, optional long clientVersion, optional androidx.health.data.client.metadata.Device? device);
+    method public String? getClientId();
+    method public long getClientVersion();
+    method public androidx.health.data.client.metadata.Device? getDevice();
+    method public java.time.Instant getLastModifiedTime();
+    method public String? getUid();
+    property public final String? clientId;
+    property public final long clientVersion;
+    property public final androidx.health.data.client.metadata.Device? device;
+    property public final java.time.Instant lastModifiedTime;
+    property public final String? uid;
+  }
+
+}
+
diff --git a/health/health-data-client/api/public_plus_experimental_current.txt b/health/health-data-client/api/public_plus_experimental_current.txt
index e6f50d0..50a64a6a 100644
--- a/health/health-data-client/api/public_plus_experimental_current.txt
+++ b/health/health-data-client/api/public_plus_experimental_current.txt
@@ -1 +1,31 @@
 // Signature format: 4.0
+package androidx.health.data.client.metadata {
+
+  public final class Device {
+    ctor public Device(optional String? identifier, optional String? manufacturer, optional String? model, optional String? type);
+    method public String? getIdentifier();
+    method public String? getManufacturer();
+    method public String? getModel();
+    method public String? getType();
+    property public final String? identifier;
+    property public final String? manufacturer;
+    property public final String? model;
+    property public final String? type;
+  }
+
+  public final class Metadata {
+    ctor public Metadata(optional String? uid, optional java.time.Instant lastModifiedTime, optional String? clientId, optional long clientVersion, optional androidx.health.data.client.metadata.Device? device);
+    method public String? getClientId();
+    method public long getClientVersion();
+    method public androidx.health.data.client.metadata.Device? getDevice();
+    method public java.time.Instant getLastModifiedTime();
+    method public String? getUid();
+    property public final String? clientId;
+    property public final long clientVersion;
+    property public final androidx.health.data.client.metadata.Device? device;
+    property public final java.time.Instant lastModifiedTime;
+    property public final String? uid;
+  }
+
+}
+
diff --git a/health/health-data-client/api/restricted_current.txt b/health/health-data-client/api/restricted_current.txt
index e6f50d0..50a64a6a 100644
--- a/health/health-data-client/api/restricted_current.txt
+++ b/health/health-data-client/api/restricted_current.txt
@@ -1 +1,31 @@
 // Signature format: 4.0
+package androidx.health.data.client.metadata {
+
+  public final class Device {
+    ctor public Device(optional String? identifier, optional String? manufacturer, optional String? model, optional String? type);
+    method public String? getIdentifier();
+    method public String? getManufacturer();
+    method public String? getModel();
+    method public String? getType();
+    property public final String? identifier;
+    property public final String? manufacturer;
+    property public final String? model;
+    property public final String? type;
+  }
+
+  public final class Metadata {
+    ctor public Metadata(optional String? uid, optional java.time.Instant lastModifiedTime, optional String? clientId, optional long clientVersion, optional androidx.health.data.client.metadata.Device? device);
+    method public String? getClientId();
+    method public long getClientVersion();
+    method public androidx.health.data.client.metadata.Device? getDevice();
+    method public java.time.Instant getLastModifiedTime();
+    method public String? getUid();
+    property public final String? clientId;
+    property public final long clientVersion;
+    property public final androidx.health.data.client.metadata.Device? device;
+    property public final java.time.Instant lastModifiedTime;
+    property public final String? uid;
+  }
+
+}
+
diff --git a/health/health-data-client/build.gradle b/health/health-data-client/build.gradle
index 6bf816c..63cbf4c 100644
--- a/health/health-data-client/build.gradle
+++ b/health/health-data-client/build.gradle
@@ -27,6 +27,12 @@
     // Add dependencies here
 }
 
+android {
+    defaultConfig {
+	minSdkVersion 26
+    }
+}
+
 androidx {
     name = "AndroidX Health Data Client Library"
     type = LibraryType.PUBLISHED_LIBRARY
diff --git a/health/health-data-client/src/main/java/androidx/health/data/client/metadata/Device.kt b/health/health-data-client/src/main/java/androidx/health/data/client/metadata/Device.kt
new file mode 100644
index 0000000..1a66ac4
--- /dev/null
+++ b/health/health-data-client/src/main/java/androidx/health/data/client/metadata/Device.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.health.data.client.metadata
+
+/**
+ * The physical device (such as phone, watch, scale, or chest strap) that captured associated health
+ * record.
+ *
+ * [Device] needs to be populated by users of the API. Fields not provided by clients will remain
+ * absent. Two devices with corresponding unknown fields will compare as equal, but may represent
+ * different devices.
+ */
+public class Device(
+    public val identifier: String? = null,
+    public val manufacturer: String? = null,
+    public val model: String? = null,
+    public val type: String? = null
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Device) return false
+
+        if (identifier != other.identifier) return false
+        if (manufacturer != other.manufacturer) return false
+        if (model != other.model) return false
+        if (type != other.type) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = identifier?.hashCode() ?: 0
+        result = 31 * result + (manufacturer?.hashCode() ?: 0)
+        result = 31 * result + (model?.hashCode() ?: 0)
+        result = 31 * result + (type?.hashCode() ?: 0)
+        return result
+    }
+}
diff --git a/health/health-data-client/src/main/java/androidx/health/data/client/metadata/Metadata.kt b/health/health-data-client/src/main/java/androidx/health/data/client/metadata/Metadata.kt
new file mode 100644
index 0000000..b5720d7
--- /dev/null
+++ b/health/health-data-client/src/main/java/androidx/health/data/client/metadata/Metadata.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.health.data.client.metadata
+
+import java.time.Instant
+
+/** Set of shared metadata fields for [androidx.health.data.client.records.Record]. */
+public class Metadata(
+    /**
+     * Unique identifier of this data, assigned by the Android Health Platform at insertion time.
+     */
+    public val uid: String? = null,
+
+    /** Automatically populated to when data was last modified (or originally created). */
+    public val lastModifiedTime: Instant = Instant.EPOCH,
+
+    /** Optional client supplied unique data identifier associated with the data. */
+    public val clientId: String? = null,
+
+    /** Optional client supplied version associated with the data. */
+    public val clientVersion: Long = 0,
+
+    /** Optional client supplied device information associated with the data. */
+    public val device: Device? = null,
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Metadata) return false
+
+        if (uid != other.uid) return false
+        if (lastModifiedTime != other.lastModifiedTime) return false
+        if (clientId != other.clientId) return false
+        if (clientVersion != other.clientVersion) return false
+        if (device != other.device) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = uid?.hashCode() ?: 0
+        result = 31 * result + lastModifiedTime.hashCode()
+        result = 31 * result + (clientId?.hashCode() ?: 0)
+        result = 31 * result + clientVersion.hashCode()
+        result = 31 * result + (device?.hashCode() ?: 0)
+        return result
+    }
+
+    internal companion object {
+        /** A default instance of metadata with no fields initialised. */
+        @JvmField internal val EMPTY = Metadata()
+    }
+}
diff --git a/lifecycle/lifecycle-viewmodel-compose/api/current.txt b/lifecycle/lifecycle-viewmodel-compose/api/current.txt
index 7ab89d3..7d21e88 100644
--- a/lifecycle/lifecycle-viewmodel-compose/api/current.txt
+++ b/lifecycle/lifecycle-viewmodel-compose/api/current.txt
@@ -13,6 +13,7 @@
     method @Deprecated @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM! viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
     method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM! viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, optional androidx.lifecycle.ViewModelProvider.Factory? factory, optional androidx.lifecycle.viewmodel.CreationExtras extras);
     method @Deprecated @androidx.compose.runtime.Composable public static <VM extends androidx.lifecycle.ViewModel> VM! viewModel(Class<VM> modelClass, optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
+    method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM! viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
   }
 
 }
diff --git a/lifecycle/lifecycle-viewmodel-compose/api/public_plus_experimental_current.txt b/lifecycle/lifecycle-viewmodel-compose/api/public_plus_experimental_current.txt
index 7ab89d3..7d21e88 100644
--- a/lifecycle/lifecycle-viewmodel-compose/api/public_plus_experimental_current.txt
+++ b/lifecycle/lifecycle-viewmodel-compose/api/public_plus_experimental_current.txt
@@ -13,6 +13,7 @@
     method @Deprecated @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM! viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
     method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM! viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, optional androidx.lifecycle.ViewModelProvider.Factory? factory, optional androidx.lifecycle.viewmodel.CreationExtras extras);
     method @Deprecated @androidx.compose.runtime.Composable public static <VM extends androidx.lifecycle.ViewModel> VM! viewModel(Class<VM> modelClass, optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
+    method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM! viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
   }
 
 }
diff --git a/lifecycle/lifecycle-viewmodel-compose/api/restricted_current.txt b/lifecycle/lifecycle-viewmodel-compose/api/restricted_current.txt
index 7ab89d3..7d21e88 100644
--- a/lifecycle/lifecycle-viewmodel-compose/api/restricted_current.txt
+++ b/lifecycle/lifecycle-viewmodel-compose/api/restricted_current.txt
@@ -13,6 +13,7 @@
     method @Deprecated @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM! viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
     method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM! viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, optional androidx.lifecycle.ViewModelProvider.Factory? factory, optional androidx.lifecycle.viewmodel.CreationExtras extras);
     method @Deprecated @androidx.compose.runtime.Composable public static <VM extends androidx.lifecycle.ViewModel> VM! viewModel(Class<VM> modelClass, optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
+    method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM! viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String key, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
   }
 
 }
diff --git a/lifecycle/lifecycle-viewmodel-compose/samples/src/main/java/androidx/lifecycle/viewmodel/compose/samples/LifecycleViewModelSamples.kt b/lifecycle/lifecycle-viewmodel-compose/samples/src/main/java/androidx/lifecycle/viewmodel/compose/samples/LifecycleViewModelSamples.kt
index c6d903ce..e228f0b 100644
--- a/lifecycle/lifecycle-viewmodel-compose/samples/src/main/java/androidx/lifecycle/viewmodel/compose/samples/LifecycleViewModelSamples.kt
+++ b/lifecycle/lifecycle-viewmodel-compose/samples/src/main/java/androidx/lifecycle/viewmodel/compose/samples/LifecycleViewModelSamples.kt
@@ -22,11 +22,14 @@
 import androidx.core.os.bundleOf
 import androidx.lifecycle.DEFAULT_ARGS_KEY
 import androidx.lifecycle.HasDefaultViewModelProviderFactory
+import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.createSavedStateHandle
 import androidx.lifecycle.viewmodel.CreationExtras
 import androidx.lifecycle.viewmodel.MutableCreationExtras
 import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
+import androidx.lifecycle.viewmodel.compose.viewModel
 
 @Sampled
 @Composable
@@ -55,4 +58,23 @@
     viewModel.args
 }
 
+@Sampled
+@Composable
+fun CreationExtrasViewModelInitializer() {
+    // Just like any call to viewModel(), the default owner is the LocalViewModelStoreOwner.current.
+    // The lambda is only called the first time the ViewModel needs to be created.
+    val viewModel = viewModel {
+        // Within the lambda, you have direct access to the CreationExtras which allows you to call
+        // extension methods on CreationExtras such as createSavedStateHandle()
+        val handle = createSavedStateHandle()
+        // You can send any custom parameter, repository, etc. to your ViewModel.
+        SavedStateViewModel(handle, "custom_value")
+    }
+    // The handle and parameter are now available from the ViewModel
+    viewModel.handle
+    viewModel.value
+}
+
 class TestViewModel(val args: String?) : ViewModel()
+
+class SavedStateViewModel(val handle: SavedStateHandle, val value: String) : ViewModel()
diff --git a/lifecycle/lifecycle-viewmodel-compose/src/androidTest/java/androidx/lifecycle/viewmodel/compose/ViewModelTest.kt b/lifecycle/lifecycle-viewmodel-compose/src/androidTest/java/androidx/lifecycle/viewmodel/compose/ViewModelTest.kt
index b89fd5c..fbd860e 100644
--- a/lifecycle/lifecycle-viewmodel-compose/src/androidTest/java/androidx/lifecycle/viewmodel/compose/ViewModelTest.kt
+++ b/lifecycle/lifecycle-viewmodel-compose/src/androidTest/java/androidx/lifecycle/viewmodel/compose/ViewModelTest.kt
@@ -135,6 +135,31 @@
     }
 
     @Test
+    public fun viewModelCreatedCreationExtrasInitializer() {
+        val owner = FakeViewModelStoreOwner()
+        var createdInComposition: Any? = null
+        val extrasKey = object : CreationExtras.Key<String> { }
+        rule.setContent {
+            CompositionLocalProvider(LocalViewModelStoreOwner provides owner) {
+                createdInComposition = viewModel(
+                    key = "test",
+                    initializer = {
+                        TestViewModel(MutableCreationExtras(this).apply {
+                            set(extrasKey, "value")
+                        })
+                    }
+                )
+            }
+        }
+
+        assertThat(owner.factory.createCalled).isFalse()
+        val createdManually =
+            ViewModelProvider(owner)["test", TestViewModel::class.java]
+        assertThat(createdInComposition).isEqualTo(createdManually)
+        assertThat(createdManually.extras[extrasKey]).isEqualTo("value")
+    }
+
+    @Test
     public fun viewModelCreatedViaDefaultFactoryWithCustomOwner() {
         val customOwner = FakeViewModelStoreOwner()
         val owner = FakeViewModelStoreOwner()
@@ -223,7 +248,7 @@
     }
 }
 
-private class TestViewModel : ViewModel()
+private class TestViewModel(val extras: CreationExtras = CreationExtras.Empty) : ViewModel()
 
 private class FakeViewModelProviderFactory : ViewModelProvider.Factory {
     var createCalled = false
diff --git a/lifecycle/lifecycle-viewmodel-compose/src/main/java/androidx/lifecycle/viewmodel/compose/ViewModel.kt b/lifecycle/lifecycle-viewmodel-compose/src/main/java/androidx/lifecycle/viewmodel/compose/ViewModel.kt
index 8978113..b08ebf1 100644
--- a/lifecycle/lifecycle-viewmodel-compose/src/main/java/androidx/lifecycle/viewmodel/compose/ViewModel.kt
+++ b/lifecycle/lifecycle-viewmodel-compose/src/main/java/androidx/lifecycle/viewmodel/compose/ViewModel.kt
@@ -22,6 +22,8 @@
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelStoreOwner
 import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.initializer
+import androidx.lifecycle.viewmodel.viewModelFactory
 
 /**
  * Returns an existing [ViewModel] or creates a new one in the given owner (usually, a fragment or
@@ -153,6 +155,43 @@
     }
 ): VM = viewModelStoreOwner.get(modelClass, key, factory, extras)
 
+/**
+ * Returns an existing [ViewModel] or creates a new one in the scope (usually, a fragment or
+ * an activity)
+ *
+ * The created [ViewModel] is associated with the given [viewModelStoreOwner] and will be retained
+ * as long as the scope is alive (e.g. if it is an activity, until it is
+ * finished or process is killed).
+ *
+ * If the [viewModelStoreOwner] implements [HasDefaultViewModelProviderFactory] its default
+ * [CreationExtras] are the ones that will be provided to the receiver scope from the [initializer]
+ *
+ * @param viewModelStoreOwner The scope that the created [ViewModel] should be associated with.
+ * @param key The key to use to identify the [ViewModel].
+ * @param initializer lambda used to create an instance of the ViewModel class
+ * @return A [ViewModel] that is an instance of the given [VM] type.
+ *
+ * @sample androidx.lifecycle.viewmodel.compose.samples.CreationExtrasViewModelInitializer
+ */
+@Composable
+public inline fun <reified VM : ViewModel> viewModel(
+    viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
+        "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
+    },
+    key: String? = null,
+    noinline initializer: CreationExtras.() -> VM
+): VM = viewModel(
+    VM::class.java,
+    viewModelStoreOwner,
+    key,
+    viewModelFactory { initializer(initializer) },
+    if (viewModelStoreOwner is HasDefaultViewModelProviderFactory) {
+        viewModelStoreOwner.defaultViewModelCreationExtras
+    } else {
+        CreationExtras.Empty
+    }
+)
+
 private fun <VM : ViewModel> ViewModelStoreOwner.get(
     javaClass: Class<VM>,
     key: String? = null,
diff --git a/lifecycle/lifecycle-viewmodel/api/current.txt b/lifecycle/lifecycle-viewmodel/api/current.txt
index 5b11ead..5b564eb 100644
--- a/lifecycle/lifecycle-viewmodel/api/current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/current.txt
@@ -13,6 +13,8 @@
 
   public abstract class ViewModel {
     ctor public ViewModel();
+    ctor public ViewModel(java.io.Closeable!...);
+    method public void addCloseable(java.io.Closeable);
     method protected void onCleared();
   }
 
diff --git a/lifecycle/lifecycle-viewmodel/api/public_plus_experimental_current.txt b/lifecycle/lifecycle-viewmodel/api/public_plus_experimental_current.txt
index 5b11ead..5b564eb 100644
--- a/lifecycle/lifecycle-viewmodel/api/public_plus_experimental_current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/public_plus_experimental_current.txt
@@ -13,6 +13,8 @@
 
   public abstract class ViewModel {
     ctor public ViewModel();
+    ctor public ViewModel(java.io.Closeable!...);
+    method public void addCloseable(java.io.Closeable);
     method protected void onCleared();
   }
 
diff --git a/lifecycle/lifecycle-viewmodel/api/restricted_current.txt b/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
index 5b11ead..5b564eb 100644
--- a/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
@@ -13,6 +13,8 @@
 
   public abstract class ViewModel {
     ctor public ViewModel();
+    ctor public ViewModel(java.io.Closeable!...);
+    method public void addCloseable(java.io.Closeable);
     method protected void onCleared();
   }
 
diff --git a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java
index 1dc643c..2370b740 100644
--- a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java
+++ b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java
@@ -17,12 +17,16 @@
 package androidx.lifecycle;
 
 import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import java.io.Closeable;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * ViewModel is a class that is responsible for preparing and managing the data for
@@ -108,9 +112,49 @@
     // Can't use ConcurrentHashMap, because it can lose values on old apis (see b/37042460)
     @Nullable
     private final Map<String, Object> mBagOfTags = new HashMap<>();
+    @Nullable
+    private final Set<Closeable> mCloseables = new LinkedHashSet<>();
     private volatile boolean mCleared = false;
 
     /**
+     * Construct a new ViewModel instance.
+     * <p>
+     * You should <strong>never</strong> manually construct a ViewModel outside of a
+     * {@link ViewModelProvider.Factory}.
+     */
+    public ViewModel() {
+    }
+
+    /**
+     * Construct a new ViewModel instance. Any {@link Closeable} objects provided here
+     * will be closed directly before {@link #onCleared()} is called.
+     * <p>
+     * You should <strong>never</strong> manually construct a ViewModel outside of a
+     * {@link ViewModelProvider.Factory}.
+     */
+    public ViewModel(@NonNull Closeable... closeables) {
+        mCloseables.addAll(Arrays.asList(closeables));
+    }
+
+    /**
+     * Add a new {@link Closeable} object that will be closed directly before
+     * {@link #onCleared()} is called.
+     *
+     * @param closeable The object that should be {@link Closeable#close() closed} directly before
+     *                  {@link #onCleared()} is called.
+     */
+    public void addCloseable(@NonNull Closeable closeable) {
+        // As this method is final, it will still be called on mock objects even
+        // though mCloseables won't actually be created...we'll just not do anything
+        // in that case.
+        if (mCloseables != null) {
+            synchronized (mCloseables) {
+                mCloseables.add(closeable);
+            }
+        }
+    }
+
+    /**
      * This method will be called when this ViewModel is no longer used and will be destroyed.
      * <p>
      * It is useful when ViewModel observes some data and you need to clear this subscription to
@@ -135,6 +179,14 @@
                 }
             }
         }
+        // We need the same null check here
+        if (mCloseables != null) {
+            synchronized (mCloseables) {
+                for (Closeable closeable : mCloseables) {
+                    closeWithRuntimeException(closeable);
+                }
+            }
+        }
         onCleared();
     }
 
diff --git a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelTest.java b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelTest.java
index 1b04323..76fd7b0 100644
--- a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelTest.java
+++ b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelTest.java
@@ -21,6 +21,8 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
+import androidx.annotation.NonNull;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -39,7 +41,13 @@
         }
     }
 
-    class ViewModel extends androidx.lifecycle.ViewModel {
+    static class ViewModel extends androidx.lifecycle.ViewModel {
+    }
+
+    static class ConstructorArgViewModel extends androidx.lifecycle.ViewModel {
+        ConstructorArgViewModel(@NonNull Closeable closeable) {
+            super(closeable);
+        }
     }
 
     @Test
@@ -73,4 +81,29 @@
         ViewModel vm = Mockito.mock(ViewModel.class);
         assertThat(vm.getTag("Careless mocks =|"), nullValue());
     }
+
+    @Test
+    public void testAddCloseable() {
+        ViewModel vm = new ViewModel();
+        CloseableImpl impl = new CloseableImpl();
+        vm.addCloseable(impl);
+        vm.clear();
+        assertTrue(impl.mWasClosed);
+    }
+
+    @Test
+    public void testConstructorCloseable() {
+        CloseableImpl impl = new CloseableImpl();
+        ConstructorArgViewModel vm = new ConstructorArgViewModel(impl);
+        vm.clear();
+        assertTrue(impl.mWasClosed);
+    }
+
+    @Test
+    public void testMockedAddCloseable() {
+        ViewModel vm = Mockito.mock(ViewModel.class);
+        CloseableImpl impl = new CloseableImpl();
+        // This shouldn't crash, even on a mocked object
+        vm.addCloseable(impl);
+    }
 }
diff --git a/lint-checks/integration-tests/src/main/java/androidx/CameraXMissingQuirkSummaryJava.java b/lint-checks/integration-tests/src/main/java/androidx/CameraXMissingQuirkSummaryJava.java
new file mode 100644
index 0000000..57d06d3
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/CameraXMissingQuirkSummaryJava.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx;
+
+/**
+ * A quirk where crop rect is wrong on samsung devices.
+ */
+public class CameraXMissingQuirkSummaryJava implements Quirk {
+
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/Quirk.kt b/lint-checks/integration-tests/src/main/java/androidx/Quirk.kt
new file mode 100644
index 0000000..7abe5d9
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/Quirk.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx
+
+interface Quirk
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidManifestServiceExportedDetector.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidManifestServiceExportedDetector.kt
new file mode 100644
index 0000000..12e1e056
--- /dev/null
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidManifestServiceExportedDetector.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.build.lint
+
+import com.android.SdkConstants
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Incident
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.XmlContext
+import com.android.tools.lint.detector.api.XmlScanner
+import org.w3c.dom.Element
+
+@Suppress("UnstableApiUsage")
+class AndroidManifestServiceExportedDetector : Detector(), XmlScanner {
+
+    override fun getApplicableElements(): Collection<String> {
+        return listOf(SdkConstants.TAG_SERVICE)
+    }
+
+    override fun visitElement(context: XmlContext, element: Element) {
+        val attrExported = element.getAttribute("android:${SdkConstants.ATTR_EXPORTED}")
+        if (attrExported != "true") {
+            val incident = Incident(context, ISSUE)
+                .message("Missing exported=true in <service> tag")
+                .at(element)
+            context.report(incident)
+        }
+    }
+
+    companion object {
+        val ISSUE = Issue.create(
+            id = "MissingServiceExportedEqualsTrue",
+            briefDescription = "Missing exported=true declaration in the <service> tag inside" +
+                " the library manifest",
+            explanation = "Library-defined services should set the exported attribute to true.",
+            category = Category.CORRECTNESS,
+            priority = 5,
+            severity = Severity.ERROR,
+            implementation = Implementation(
+                AndroidManifestServiceExportedDetector::class.java,
+                Scope.MANIFEST_SCOPE
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
index 0ac4c28..74fcf64 100644
--- a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
@@ -38,6 +38,7 @@
     companion object {
         val Issues get(): List<Issue> {
             return listOf(
+                AndroidManifestServiceExportedDetector.ISSUE,
                 BanParcelableUsage.ISSUE,
                 BanConcurrentHashMap.ISSUE,
                 BanInappropriateExperimentalUsage.ISSUE,
@@ -60,6 +61,7 @@
                 PrivateConstructorForUtilityClassDetector.ISSUE,
                 ClassVerificationFailureDetector.ISSUE,
                 IdeaSuppressionDetector.ISSUE,
+                CameraXQuirksClassDetector.ISSUE
             )
         }
     }
diff --git a/lint-checks/src/main/java/androidx/build/lint/CameraXQuirksClassDetector.kt b/lint-checks/src/main/java/androidx/build/lint/CameraXQuirksClassDetector.kt
new file mode 100644
index 0000000..11591b4
--- /dev/null
+++ b/lint-checks/src/main/java/androidx/build/lint/CameraXQuirksClassDetector.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.build.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UClass
+
+/**
+ * Detector to apply lint rules for CameraX quirks. The rule is to enforce a javadoc template to
+ * describe the bug id, issue description and device info. This detector is disabled by default.
+ * Only CameraX modules will enable the detector.
+ */
+class CameraXQuirksClassDetector : Detector(), Detector.UastScanner {
+
+    override fun getApplicableUastTypes() = listOf(UClass::class.java)
+
+    override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
+
+        override fun visitClass(node: UClass) {
+            val elements = node.implementsList?.getReferenceElements()
+            var isQuirk = false
+            if (elements != null) {
+                for (element in elements) {
+                    if (("Quirk").equals(element.referenceName)) {
+                        isQuirk = true
+                    }
+                }
+            }
+
+            if (isQuirk) {
+                val comments = node.comments
+                val sb = StringBuilder()
+                comments.forEach { sb.append(it.text) }
+                val comment = sb.append("\n").toString()
+
+                if (!comment.contains("@QuirkSummary")) {
+                    val implForInsertion = """
+                         * @QuirkSummary
+                         *     Bug Id:
+                         *     Description:
+                         *     Device(s):
+                        """.trimIndent()
+
+                    context.report(
+                        CameraXQuirksClassDetector.ISSUE, node,
+                        context.getNameLocation(node),
+                        "CameraX quirks should include this template in the javadoc:" +
+                            "\n\n$implForInsertion\n\n"
+                    )
+                }
+            }
+        }
+    }
+
+    companion object {
+        val ISSUE = Issue.create(
+            id = "CameraXQuirksClassDetector",
+            briefDescription = "CameraQuirks include @QuirkSummary in the javadoc",
+            explanation = "CameraX quirks should include @QuirkSummary in the javadoc.",
+            category = Category.CORRECTNESS,
+            priority = 5,
+            severity = Severity.ERROR,
+            enabledByDefault = false,
+            implementation = Implementation(
+                CameraXQuirksClassDetector::class.java,
+                Scope.JAVA_FILE_SCOPE
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/lint-checks/src/test/java/androidx/build/lint/AndroidManifestServiceExportedDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/AndroidManifestServiceExportedDetectorTest.kt
new file mode 100644
index 0000000..1a482ac
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/AndroidManifestServiceExportedDetectorTest.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class AndroidManifestServiceExportedDetectorTest : AbstractLintDetectorTest(
+    useDetector = AndroidManifestServiceExportedDetector(),
+    useIssues = listOf(AndroidManifestServiceExportedDetector.ISSUE),
+) {
+
+    @Test
+    fun `Detect missing exported=true declaration in service tag`() {
+        val input = arrayOf(
+            manifestSample()
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+AndroidManifest.xml:22: Error: Missing exported=true in <service> tag [MissingServiceExportedEqualsTrue]
+        <service android:name="androidx.core.app.JobIntentService">
+        ^
+1 errors, 0 warnings
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected)
+    }
+
+    @Test
+    fun `Detect present exported=true declaration in service tag`() {
+        val input = xml(
+            "AndroidManifest.xml",
+            """
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <application>
+        <service
+            android:name="androidx.service"
+            android:exported="true" />
+    </application>
+</manifest>
+                """.trimIndent()
+        )
+
+        check(input).expectClean()
+    }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/CameraXQuirksClassDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/CameraXQuirksClassDetectorTest.kt
new file mode 100644
index 0000000..2f66733
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/CameraXQuirksClassDetectorTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class CameraXQuirksClassDetectorTest : AbstractLintDetectorTest(
+    useDetector = CameraXQuirksClassDetector(),
+    useIssues = listOf(CameraXQuirksClassDetector.ISSUE)
+) {
+
+    @Test
+    fun `Detection of CameraX Quirks in Java`() {
+        val input = arrayOf(
+            javaSample("androidx.CameraXMissingQuirkSummaryJava")
+        )
+
+        /* ktlint-disable max-line-length */
+        val expected = """
+            src/androidx/CameraXMissingQuirkSummaryJava.java:22: Error: CameraX quirks should include this template in the javadoc:
+
+            * @QuirkSummary
+            *     Bug Id:
+            *     Description:
+            *     Device(s):
+
+             [CameraXQuirksClassDetector]
+            public class CameraXMissingQuirkSummaryJava implements Quirk {
+                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+            1 errors, 0 warnings
+        """.trimIndent()
+        /* ktlint-enable max-line-length */
+
+        check(*input).expect(expected)
+    }
+}
\ No newline at end of file
diff --git a/media/media/src/main/java/androidx/media/MediaSessionManagerImplBase.java b/media/media/src/main/java/androidx/media/MediaSessionManagerImplBase.java
index a5d7bc5..6a38578 100644
--- a/media/media/src/main/java/androidx/media/MediaSessionManagerImplBase.java
+++ b/media/media/src/main/java/androidx/media/MediaSessionManagerImplBase.java
@@ -53,6 +53,7 @@
     }
 
     @Override
+    @SuppressWarnings("deprecation")
     public boolean isTrustedForMediaControl(
             @NonNull MediaSessionManager.RemoteUserInfoImpl userInfo) {
         try {
diff --git a/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java b/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
index f131f65..10d515c 100644
--- a/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
+++ b/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
@@ -294,6 +294,7 @@
         return null;
     }
 
+    @SuppressWarnings("deprecation")
     private static ComponentName getServiceComponentByAction(Context context, String action) {
         PackageManager pm = context.getPackageManager();
         Intent queryIntent = new Intent(action);
diff --git a/media2/integration-tests/testapp/src/main/AndroidManifest.xml b/media2/integration-tests/testapp/src/main/AndroidManifest.xml
index a2959fa..efb4e45 100644
--- a/media2/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/media2/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -47,8 +47,11 @@
             </intent-filter>
         </activity>
 
-        <service android:name=".VideoSessionService" android:exported="false"
-                 android:process=":VideoSessionService">
+        <service
+            android:name=".VideoSessionService"
+            android:exported="false"
+            android:process=":VideoSessionService"
+            tools:ignore="MissingServiceExportedEqualsTrue">
             <intent-filter>
                 <action android:name="androidx.media2.session.MediaSessionService" />
             </intent-filter>
diff --git a/media2/media2-session/src/main/java/androidx/media2/session/MediaSessionImplBase.java b/media2/media2-session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
index 20d9a96..34f5ad0 100644
--- a/media2/media2-session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
+++ b/media2/media2-session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
@@ -1153,6 +1153,7 @@
     }
 
     @Nullable
+    @SuppressWarnings("deprecation")
     private ComponentName getServiceComponentByAction(@NonNull String action) {
         PackageManager pm = mContext.getPackageManager();
         Intent queryIntent = new Intent(action);
diff --git a/media2/media2-session/src/main/java/androidx/media2/session/MediaSessionManager.java b/media2/media2-session/src/main/java/androidx/media2/session/MediaSessionManager.java
index c689c10..d628d7b 100644
--- a/media2/media2-session/src/main/java/androidx/media2/session/MediaSessionManager.java
+++ b/media2/media2-session/src/main/java/androidx/media2/session/MediaSessionManager.java
@@ -102,6 +102,7 @@
      * @return set of tokens
      */
     @NonNull
+    @SuppressWarnings("deprecation")
     public Set<SessionToken> getSessionServiceTokens() {
         ArraySet<SessionToken> sessionServiceTokens = new ArraySet<>();
         PackageManager pm = mContext.getPackageManager();
diff --git a/media2/media2-session/src/main/java/androidx/media2/session/SessionToken.java b/media2/media2-session/src/main/java/androidx/media2/session/SessionToken.java
index 3cf63b6..df3d6da 100644
--- a/media2/media2-session/src/main/java/androidx/media2/session/SessionToken.java
+++ b/media2/media2-session/src/main/java/androidx/media2/session/SessionToken.java
@@ -353,6 +353,7 @@
         }
     }
 
+    @SuppressWarnings("deprecation")
     private static boolean isInterfaceDeclared(PackageManager manager, String serviceInterface,
             ComponentName serviceComponent) {
         Intent serviceIntent = new Intent(serviceInterface);
@@ -378,6 +379,7 @@
         return false;
     }
 
+    @SuppressWarnings("deprecation")
     private static int getUid(PackageManager manager, String packageName) {
         try {
             return manager.getApplicationInfo(packageName, 0).uid;
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
index 43c29f4..88a95b7 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
@@ -412,6 +412,7 @@
         return result;
     }
 
+    @SuppressWarnings("deprecation")
     private boolean showOutputSwitcherForAndroidR() {
         Context context = getContext();
 
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RegisteredMediaRouteProviderWatcher.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RegisteredMediaRouteProviderWatcher.java
index cb299313..d392178 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RegisteredMediaRouteProviderWatcher.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RegisteredMediaRouteProviderWatcher.java
@@ -95,6 +95,7 @@
         }
     }
 
+    @SuppressWarnings("deprecation")
     void scanPackages() {
         if (!mRunning) {
             return;
@@ -164,6 +165,7 @@
 
     @RequiresApi(Build.VERSION_CODES.R)
     @NonNull
+    @SuppressWarnings("deprecation")
     List<ServiceInfo> getMediaRoute2ProviderServices() {
         Intent intent = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
 
diff --git a/navigation/navigation-fragment/api/api_lint.ignore b/navigation/navigation-fragment/api/api_lint.ignore
index ff18170..b57faea 100644
--- a/navigation/navigation-fragment/api/api_lint.ignore
+++ b/navigation/navigation-fragment/api/api_lint.ignore
@@ -3,9 +3,9 @@
     Method FragmentNavArgsLazyKt.navArgs appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
 
 
-MissingNullability: androidx.navigation.NavGraphViewModelLazyKt#navGraphViewModels(androidx.fragment.app.Fragment, String, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
+MissingNullability: androidx.navigation.NavGraphViewModelLazyKt#navGraphViewModels(androidx.fragment.app.Fragment, String, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
     Missing nullability on method `navGraphViewModels` return
-MissingNullability: androidx.navigation.NavGraphViewModelLazyKt#navGraphViewModels(androidx.fragment.app.Fragment, int, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
+MissingNullability: androidx.navigation.NavGraphViewModelLazyKt#navGraphViewModels(androidx.fragment.app.Fragment, int, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
     Missing nullability on method `navGraphViewModels` return
 MissingNullability: androidx.navigation.fragment.FragmentNavArgsLazyKt#navArgs(androidx.fragment.app.Fragment):
     Missing nullability on method `navArgs` return
diff --git a/navigation/navigation-fragment/api/current.txt b/navigation/navigation-fragment/api/current.txt
index 98ee0fc..52fc882 100644
--- a/navigation/navigation-fragment/api/current.txt
+++ b/navigation/navigation-fragment/api/current.txt
@@ -2,8 +2,10 @@
 package androidx.navigation {
 
   public final class NavGraphViewModelLazyKt {
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
   }
 
 }
diff --git a/navigation/navigation-fragment/api/public_plus_experimental_current.txt b/navigation/navigation-fragment/api/public_plus_experimental_current.txt
index 98ee0fc..52fc882 100644
--- a/navigation/navigation-fragment/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-fragment/api/public_plus_experimental_current.txt
@@ -2,8 +2,10 @@
 package androidx.navigation {
 
   public final class NavGraphViewModelLazyKt {
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
   }
 
 }
diff --git a/navigation/navigation-fragment/api/restricted_current.txt b/navigation/navigation-fragment/api/restricted_current.txt
index 98ee0fc..52fc882 100644
--- a/navigation/navigation-fragment/api/restricted_current.txt
+++ b/navigation/navigation-fragment/api/restricted_current.txt
@@ -2,8 +2,10 @@
 package androidx.navigation {
 
   public final class NavGraphViewModelLazyKt {
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
-    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM>! navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
   }
 
 }
diff --git a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt
index 70496b2..07ce168f 100644
--- a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt
+++ b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt
@@ -21,13 +21,19 @@
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import androidx.core.os.bundleOf
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentActivity
 import androidx.fragment.app.testing.launchFragmentInContainer
 import androidx.fragment.app.testing.withFragment
+import androidx.lifecycle.DEFAULT_ARGS_KEY
 import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.SavedStateViewModelFactory
 import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelStore
+import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.MutableCreationExtras
 import androidx.navigation.NavHostController
 import androidx.navigation.Navigation
 import androidx.navigation.createGraph
@@ -82,6 +88,30 @@
     }
 
     @Test
+    fun vmInitializationRoute() {
+        val scenario = launchFragmentInContainer<TestRouteVMFragment>()
+        navController.setViewModelStore(ViewModelStore())
+        scenario.onFragment { fragment ->
+            Navigation.setViewNavController(fragment.requireView(), navController)
+        }
+        val navGraph = navController.navigatorProvider.navigation(
+            route = "vm_graph",
+            startDestination = "start_destination"
+        ) {
+            test("start_destination")
+        }
+        scenario.withFragment {
+            navController.setGraph(navGraph, null)
+        }
+
+        scenario.onFragment { fragment ->
+            assertThat(fragment.viewModel).isNotNull()
+            assertThat(fragment.savedStateViewModelCE).isNotNull()
+            assertThat(fragment.savedStateViewModelCE.defaultValue).isEqualTo("value")
+        }
+    }
+
+    @Test
     fun sameViewModelAcrossFragments() {
         with(ActivityScenario.launch(NavGraphActivity::class.java)) {
             val navController = withActivity { findNavController(R.id.nav_host_fragment) }
@@ -238,6 +268,9 @@
 class TestRouteVMFragment : Fragment() {
     val viewModel: TestViewModel by navGraphViewModels("vm_graph")
     val savedStateViewModel: TestSavedStateViewModel by navGraphViewModels("vm_graph")
+    val savedStateViewModelCE: TestSavedStateViewModel by navGraphViewModels("vm_graph",
+        extrasProducer = { defaultViewModelCreationExtras }
+    )
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
@@ -245,7 +278,19 @@
     ): View? {
         return View(activity)
     }
+
+    override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory {
+        return SavedStateViewModelFactory()
+    }
+
+    override fun getDefaultViewModelCreationExtras(): CreationExtras {
+        val extras = MutableCreationExtras(super.getDefaultViewModelCreationExtras())
+        extras[DEFAULT_ARGS_KEY] = bundleOf("test" to "value")
+        return extras
+    }
 }
 
 class TestViewModel : ViewModel()
-class TestSavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel()
+class TestSavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel() {
+    val defaultValue = savedStateHandle.get<String>("test")
+}
diff --git a/navigation/navigation-fragment/src/main/java/androidx/navigation/NavGraphViewModelLazy.kt b/navigation/navigation-fragment/src/main/java/androidx/navigation/NavGraphViewModelLazy.kt
index 08e165e..79eb8ac 100644
--- a/navigation/navigation-fragment/src/main/java/androidx/navigation/NavGraphViewModelLazy.kt
+++ b/navigation/navigation-fragment/src/main/java/androidx/navigation/NavGraphViewModelLazy.kt
@@ -23,11 +23,12 @@
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelStore
+import androidx.lifecycle.viewmodel.CreationExtras
 import androidx.navigation.fragment.findNavController
 
 /**
  * Returns a property delegate to access a [ViewModel] scoped to a navigation graph present on the
- * {@link NavController} back stack:
+ * [NavController] back stack:
  * ```
  * class MyFragment : Fragment() {
  *     val viewmodel: MainViewModel by navGraphViewModels(R.id.main)
@@ -46,7 +47,14 @@
  * and an attempt access prior to that will result in an IllegalArgumentException.
  *
  * @param navGraphId ID of a NavGraph that exists on the [NavController] back stack
+ * @param factoryProducer lambda that will be called during initialization to return
+ * [ViewModelProvider.Factory]. If none is provided, this will use the factory from the
+ * [NavBackStackEntry] referenced by the [navGraphId].
  */
+@Deprecated(
+    "Superseded by navGraphViewModels that takes a CreationExtras producer",
+    level = DeprecationLevel.HIDDEN
+)
 @MainThread
 public inline fun <reified VM : ViewModel> Fragment.navGraphViewModels(
     @IdRes navGraphId: Int,
@@ -60,13 +68,61 @@
     }
     return createViewModelLazy(
         VM::class, storeProducer,
+        { backStackEntry.defaultViewModelCreationExtras },
         factoryProducer ?: { backStackEntry.defaultViewModelProviderFactory }
-    ) { backStackEntry.defaultViewModelCreationExtras }
+    )
 }
 
 /**
  * Returns a property delegate to access a [ViewModel] scoped to a navigation graph present on the
- * {@link NavController} back stack:
+ * [NavController] back stack:
+ * ```
+ * class MyFragment : Fragment() {
+ *     val viewmodel: MainViewModel by navGraphViewModels(R.id.main)
+ * }
+ * ```
+ *
+ * Custom [ViewModelProvider.Factory] can be defined via [factoryProducer] parameter,
+ * factory returned by it will be used to create [ViewModel]:
+ * ```
+ * class MyFragment : Fragment() {
+ *     val viewmodel: MainViewModel by navGraphViewModels(R.id.main) { myFactory }
+ * }
+ * ```
+ *
+ * This property can be accessed only after this NavGraph is on the NavController back stack,
+ * and an attempt access prior to that will result in an IllegalArgumentException.
+ *
+ * @param navGraphId ID of a NavGraph that exists on the [NavController] back stack
+ * @param extrasProducer lambda that will be called during initialization to return
+ * [CreationExtras]. If none is provided, this will use the extras from the [NavBackStackEntry]
+ * referenced by the [navGraphId].
+ * @param factoryProducer lambda that will be called during initialization to return
+ * [ViewModelProvider.Factory]. If none is provided, this will use the factory from the
+ * [NavBackStackEntry] referenced by the [navGraphId].
+ */
+@MainThread
+public inline fun <reified VM : ViewModel> Fragment.navGraphViewModels(
+    @IdRes navGraphId: Int,
+    noinline extrasProducer: (() -> CreationExtras)? = null,
+    noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
+): Lazy<VM> {
+    val backStackEntry by lazy {
+        findNavController().getBackStackEntry(navGraphId)
+    }
+    val storeProducer: () -> ViewModelStore = {
+        backStackEntry.viewModelStore
+    }
+    return createViewModelLazy(
+        VM::class, storeProducer,
+        { extrasProducer?.invoke() ?: backStackEntry.defaultViewModelCreationExtras },
+        factoryProducer ?: { backStackEntry.defaultViewModelProviderFactory }
+    )
+}
+
+/**
+ * Returns a property delegate to access a [ViewModel] scoped to a navigation graph present on the
+ * [NavController] back stack:
  * ```
  * class MyFragment : Fragment() {
  *     val viewModel: MainViewModel by navGraphViewModels("main")
@@ -87,7 +143,14 @@
  * @param navGraphRoute [NavDestination.route] of a NavGraph that exists on the [NavController]
  * back stack. If a [NavDestination] with the given route does not exist on the back stack, an
  * [IllegalArgumentException] will be thrown.
+ * @param factoryProducer lambda that will be called during initialization to return
+ * [ViewModelProvider.Factory]. If none is provided, this will use the factory from the
+ * [NavBackStackEntry] referenced by the [navGraphRoute].
  */
+@Deprecated(
+    "Superseded by navGraphViewModels that takes a CreationExtras producer",
+    level = DeprecationLevel.HIDDEN
+)
 @MainThread
 public inline fun <reified VM : ViewModel> Fragment.navGraphViewModels(
     navGraphRoute: String,
@@ -101,6 +164,56 @@
     }
     return createViewModelLazy(
         VM::class, storeProducer,
+        { backStackEntry.defaultViewModelCreationExtras },
         factoryProducer ?: { backStackEntry.defaultViewModelProviderFactory }
-    ) { backStackEntry.defaultViewModelCreationExtras }
-}
\ No newline at end of file
+    )
+}
+
+/**
+ * Returns a property delegate to access a [ViewModel] scoped to a navigation graph present on the
+ * [NavController] back stack:
+ * ```
+ * class MyFragment : Fragment() {
+ *     val viewModel: MainViewModel by navGraphViewModels("main")
+ * }
+ * ```
+ *
+ * Custom [ViewModelProvider.Factory] can be defined via [factoryProducer] parameter,
+ * factory returned by it will be used to create [ViewModel]:
+ * ```
+ * class MyFragment : Fragment() {
+ *     val viewModel: MainViewModel by navGraphViewModels("main") { myFactory }
+ * }
+ * ```
+ *
+ * This property can be accessed only after this NavGraph is on the NavController back stack,
+ * and an attempt access prior to that will result in an IllegalArgumentException.
+ *
+ * @param navGraphRoute [NavDestination.route] of a NavGraph that exists on the [NavController]
+ * back stack. If a [NavDestination] with the given route does not exist on the back stack, an
+ * [IllegalArgumentException] will be thrown.
+ * @param extrasProducer lambda that will be called during initialization to return
+ * [CreationExtras]. If none is provided, this will use the extras from the [NavBackStackEntry]
+ * referenced by the [navGraphRoute].
+ * @param factoryProducer lambda that will be called during initialization to return
+ * [ViewModelProvider.Factory]. If none is provided, this will use the factory from the
+ * [NavBackStackEntry] referenced by the [navGraphRoute].
+ */
+@MainThread
+public inline fun <reified VM : ViewModel> Fragment.navGraphViewModels(
+    navGraphRoute: String,
+    noinline extrasProducer: (() -> CreationExtras)? = null,
+    noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
+): Lazy<VM> {
+    val backStackEntry by lazy {
+        findNavController().getBackStackEntry(navGraphRoute)
+    }
+    val storeProducer: () -> ViewModelStore = {
+        backStackEntry.viewModelStore
+    }
+    return createViewModelLazy(
+        VM::class, storeProducer,
+        { extrasProducer?.invoke() ?: backStackEntry.defaultViewModelCreationExtras },
+        factoryProducer ?: { backStackEntry.defaultViewModelProviderFactory }
+    )
+}
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
index 83a768a..f6fe2f6 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
+++ b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
@@ -32,6 +32,7 @@
 import java.io.File
 import java.util.Locale
 import javax.inject.Inject
+import org.jetbrains.kotlin.gradle.utils.property
 
 private const val PLUGIN_DIRNAME = "navigation-args"
 internal const val GENERATED_PATH = "generated/source/$PLUGIN_DIRNAME"
@@ -102,7 +103,18 @@
                         providerFactory.provider { variant.applicationId }
                     }
                 )
-                task.rFilePackage.set(variant.rFilePackage())
+                val rPackage = variant.rFilePackage(project)
+                task.rFilePackage.set(
+                    // If a package name is available we use that by default to ensure we
+                    // continue to support different productFlavors
+                    if (rPackage.get().isNotEmpty()) {
+                        rPackage
+                    } else {
+                        // otherwise, we fall back to the applicationId set on the task to ensure
+                        // we support namespaces as well.
+                        task.applicationId
+                    }
+                )
                 task.navigationFiles.setFrom(navigationFiles(variant, project))
                 task.outputDir.set(File(project.buildDir, "$GENERATED_PATH/${variant.dirName}"))
                 task.incrementalFolder.set(File(project.buildDir, "$INCREMENTAL_PATH/${task.name}"))
@@ -124,12 +136,16 @@
     }
 
     @Suppress("DEPRECATION") // For BaseVariant should be replaced in later studio versions
-    private fun com.android.build.gradle.api.BaseVariant.rFilePackage() = providerFactory.provider {
+    private fun com.android.build.gradle.api.BaseVariant.rFilePackage(
+        project: Project
+    ): Provider<String> = project.objects.property(String::class.java).apply {
         val mainSourceSet = sourceSets.find { it.name == "main" }
         val sourceSet = mainSourceSet ?: sourceSets[0]
         val manifest = sourceSet.manifestFile
         val parsed = XmlSlurper(false, false).parse(manifest)
-        parsed.getProperty("@package").toString()
+        set(parsed.getProperty("@package").toString())
+        disallowChanges()
+        finalizeValueOnRead()
     }
 
     @Suppress("DEPRECATION") // For BaseVariant should be replaced in later studio versions
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt b/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt
index cc612c6..5e42841 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt
+++ b/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt
@@ -89,6 +89,9 @@
                 }
             """.trimIndent(),
             suffix = """
+                android {
+                    namespace 'androidx.navigation.testapp'
+                }
                 dependencies {
                     implementation "${projectSetup.props.navigationRuntime}"
                 }
@@ -134,6 +137,9 @@
                 }
             """.trimIndent(),
             suffix = """
+                android {
+                    namespace 'androidx.navigation.testapp'
+                }
                 dependencies {
                     implementation "${projectSetup.props.kotlinStblib}"
                     implementation "${projectSetup.props.navigationRuntime}"
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/test/test-data/app-project-kotlin/src/main/AndroidManifest.xml b/navigation/navigation-safe-args-gradle-plugin/src/test/test-data/app-project-kotlin/src/main/AndroidManifest.xml
index 8cf7a88..ec8ad26 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/test/test-data/app-project-kotlin/src/main/AndroidManifest.xml
+++ b/navigation/navigation-safe-args-gradle-plugin/src/test/test-data/app-project-kotlin/src/main/AndroidManifest.xml
@@ -14,6 +14,5 @@
   ~ 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.navigation.testapp">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 </manifest>
diff --git a/paging/integration-tests/testapp/build.gradle b/paging/integration-tests/testapp/build.gradle
index d5e19f0..bb064965 100644
--- a/paging/integration-tests/testapp/build.gradle
+++ b/paging/integration-tests/testapp/build.gradle
@@ -61,3 +61,9 @@
         freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn"]
     }
 }
+
+// Enable parameter names to support Room incremental when its a project() dep.
+// See b/198431380
+tasks.withType(JavaCompile) {
+    options.compilerArgs << "-parameters"
+}
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index 0329de3..ff1bc3f 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -25,7 +25,7 @@
 kotlin.code.style=official
 # Disable docs
 androidx.enableDocumentation=false
-androidx.playground.snapshotBuildId=8145911
+androidx.playground.snapshotBuildId=8187875
 androidx.playground.metalavaBuildId=8073933
 androidx.playground.dokkaBuildId=7472101
 androidx.studio.type=playground
diff --git a/profileinstaller/profileinstaller/api/current.txt b/profileinstaller/profileinstaller/api/current.txt
index 4fb75af..4d5e335 100644
--- a/profileinstaller/profileinstaller/api/current.txt
+++ b/profileinstaller/profileinstaller/api/current.txt
@@ -5,6 +5,7 @@
     ctor public ProfileInstallReceiver();
     method public void onReceive(android.content.Context, android.content.Intent?);
     field public static final String ACTION_INSTALL_PROFILE = "androidx.profileinstaller.action.INSTALL_PROFILE";
+    field public static final String ACTION_SKIP_FILE = "androidx.profileinstaller.action.SKIP_FILE";
   }
 
   public class ProfileInstaller {
@@ -16,7 +17,9 @@
     field public static final int DIAGNOSTIC_REF_PROFILE_EXISTS = 3; // 0x3
     field public static final int RESULT_ALREADY_INSTALLED = 2; // 0x2
     field public static final int RESULT_BASELINE_PROFILE_NOT_FOUND = 6; // 0x6
+    field public static final int RESULT_DELETE_SKIP_FILE_SUCCESS = 11; // 0xb
     field public static final int RESULT_DESIRED_FORMAT_UNSUPPORTED = 5; // 0x5
+    field public static final int RESULT_INSTALL_SKIP_FILE_SUCCESS = 10; // 0xa
     field public static final int RESULT_INSTALL_SUCCESS = 1; // 0x1
     field public static final int RESULT_IO_EXCEPTION = 7; // 0x7
     field public static final int RESULT_META_FILE_REQUIRED_BUT_NOT_FOUND = 9; // 0x9
diff --git a/profileinstaller/profileinstaller/api/public_plus_experimental_current.txt b/profileinstaller/profileinstaller/api/public_plus_experimental_current.txt
index 4fb75af..4d5e335 100644
--- a/profileinstaller/profileinstaller/api/public_plus_experimental_current.txt
+++ b/profileinstaller/profileinstaller/api/public_plus_experimental_current.txt
@@ -5,6 +5,7 @@
     ctor public ProfileInstallReceiver();
     method public void onReceive(android.content.Context, android.content.Intent?);
     field public static final String ACTION_INSTALL_PROFILE = "androidx.profileinstaller.action.INSTALL_PROFILE";
+    field public static final String ACTION_SKIP_FILE = "androidx.profileinstaller.action.SKIP_FILE";
   }
 
   public class ProfileInstaller {
@@ -16,7 +17,9 @@
     field public static final int DIAGNOSTIC_REF_PROFILE_EXISTS = 3; // 0x3
     field public static final int RESULT_ALREADY_INSTALLED = 2; // 0x2
     field public static final int RESULT_BASELINE_PROFILE_NOT_FOUND = 6; // 0x6
+    field public static final int RESULT_DELETE_SKIP_FILE_SUCCESS = 11; // 0xb
     field public static final int RESULT_DESIRED_FORMAT_UNSUPPORTED = 5; // 0x5
+    field public static final int RESULT_INSTALL_SKIP_FILE_SUCCESS = 10; // 0xa
     field public static final int RESULT_INSTALL_SUCCESS = 1; // 0x1
     field public static final int RESULT_IO_EXCEPTION = 7; // 0x7
     field public static final int RESULT_META_FILE_REQUIRED_BUT_NOT_FOUND = 9; // 0x9
diff --git a/profileinstaller/profileinstaller/api/restricted_current.txt b/profileinstaller/profileinstaller/api/restricted_current.txt
index 4fb75af..4d5e335 100644
--- a/profileinstaller/profileinstaller/api/restricted_current.txt
+++ b/profileinstaller/profileinstaller/api/restricted_current.txt
@@ -5,6 +5,7 @@
     ctor public ProfileInstallReceiver();
     method public void onReceive(android.content.Context, android.content.Intent?);
     field public static final String ACTION_INSTALL_PROFILE = "androidx.profileinstaller.action.INSTALL_PROFILE";
+    field public static final String ACTION_SKIP_FILE = "androidx.profileinstaller.action.SKIP_FILE";
   }
 
   public class ProfileInstaller {
@@ -16,7 +17,9 @@
     field public static final int DIAGNOSTIC_REF_PROFILE_EXISTS = 3; // 0x3
     field public static final int RESULT_ALREADY_INSTALLED = 2; // 0x2
     field public static final int RESULT_BASELINE_PROFILE_NOT_FOUND = 6; // 0x6
+    field public static final int RESULT_DELETE_SKIP_FILE_SUCCESS = 11; // 0xb
     field public static final int RESULT_DESIRED_FORMAT_UNSUPPORTED = 5; // 0x5
+    field public static final int RESULT_INSTALL_SKIP_FILE_SUCCESS = 10; // 0xa
     field public static final int RESULT_INSTALL_SUCCESS = 1; // 0x1
     field public static final int RESULT_IO_EXCEPTION = 7; // 0x7
     field public static final int RESULT_META_FILE_REQUIRED_BUT_NOT_FOUND = 9; // 0x9
diff --git a/profileinstaller/profileinstaller/src/main/AndroidManifest.xml b/profileinstaller/profileinstaller/src/main/AndroidManifest.xml
index 04b8330..bd17478 100644
--- a/profileinstaller/profileinstaller/src/main/AndroidManifest.xml
+++ b/profileinstaller/profileinstaller/src/main/AndroidManifest.xml
@@ -35,6 +35,9 @@
             <intent-filter>
                 <action android:name="androidx.profileinstaller.action.INSTALL_PROFILE" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="androidx.profileinstaller.action.SKIP_FILE" />
+            </intent-filter>
         </receiver>
     </application>
 </manifest>
\ No newline at end of file
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstallReceiver.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstallReceiver.java
index 2532545..6de83a3 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstallReceiver.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstallReceiver.java
@@ -19,6 +19,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Bundle;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -34,17 +35,50 @@
  */
 public class ProfileInstallReceiver extends BroadcastReceiver {
     /**
-     * This is the action constant that this broadcast receiver responds to.
+     * This is the action constant that this broadcast receiver responds to and installs a profile.
      */
     public static final @NonNull String ACTION_INSTALL_PROFILE =
             "androidx.profileinstaller.action.INSTALL_PROFILE";
 
+    /**
+     * This is an action constant which requests that {@link ProfileInstaller} manipulate the
+     * skip file used during profile installation. This is only useful when the app is being
+     * instrumented when using Jetpack Macrobenchmarks.
+     */
+    public static final @NonNull String ACTION_SKIP_FILE =
+            "androidx.profileinstaller.action.SKIP_FILE";
+
+    /**
+     * This is the key in the {@link Bundle} of extras, which provides additional information on
+     * the operation to be performed.
+     */
+    private static final @NonNull String EXTRA_SKIP_FILE_OPERATION = "EXTRA_SKIP_FILE_OPERATION";
+
+    /**
+     * The value that requests that a skip file be written.
+     */
+    private static final @NonNull String EXTRA_SKIP_FILE_OPERATION_WRITE = "WRITE_SKIP_FILE";
+    /**
+     * The value that requests that a skip file be deleted.
+     */
+    private static final @NonNull String EXTRA_SKIP_FILE_OPERATION_DELETE = "DELETE_SKIP_FILE";
+
     @Override
     public void onReceive(@NonNull Context context, @Nullable Intent intent) {
         if (intent == null) return;
-        if (!ACTION_INSTALL_PROFILE.equals(intent.getAction())) return;
-        ProfileInstaller.writeProfile(context, Runnable::run,
-                new ResultDiagnostics(), /* forceWriteProfile */true);
+        String action = intent.getAction();
+        if (ACTION_INSTALL_PROFILE.equals(action)) {
+            ProfileInstaller.writeProfile(context, Runnable::run,
+                    new ResultDiagnostics(), /* forceWriteProfile */true);
+        } else if (ACTION_SKIP_FILE.equals(action)) {
+            Bundle extras = intent.getExtras();
+            String operation = extras.getString(EXTRA_SKIP_FILE_OPERATION);
+            if (EXTRA_SKIP_FILE_OPERATION_WRITE.equals(operation)) {
+                ProfileInstaller.writeSkipFile(context, Runnable::run, new ResultDiagnostics());
+            } else if (EXTRA_SKIP_FILE_OPERATION_DELETE.equals(operation)) {
+                ProfileInstaller.deleteSkipFile(context, Runnable::run, new ResultDiagnostics());
+            }
+        }
     }
 
     class ResultDiagnostics implements ProfileInstaller.DiagnosticsCallback {
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstaller.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstaller.java
index 4312dd1..83ca965 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstaller.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstaller.java
@@ -159,6 +159,10 @@
                     break;
                 case RESULT_PARSE_EXCEPTION: msg = "RESULT_PARSE_EXCEPTION";
                     break;
+                case RESULT_INSTALL_SKIP_FILE_SUCCESS: msg = "RESULT_INSTALL_SKIP_FILE_SUCCESS";
+                    break;
+                case RESULT_DELETE_SKIP_FILE_SUCCESS: msg = "RESULT_DELETE_SKIP_FILE_SUCCESS";
+                    break;
             }
 
             switch (code) {
@@ -227,7 +231,9 @@
             RESULT_BASELINE_PROFILE_NOT_FOUND,
             RESULT_IO_EXCEPTION,
             RESULT_PARSE_EXCEPTION,
-            RESULT_META_FILE_REQUIRED_BUT_NOT_FOUND
+            RESULT_META_FILE_REQUIRED_BUT_NOT_FOUND,
+            RESULT_INSTALL_SKIP_FILE_SUCCESS,
+            RESULT_DELETE_SKIP_FILE_SUCCESS
     })
     public @interface ResultCode {}
 
@@ -288,6 +294,16 @@
     @ResultCode public static final int RESULT_META_FILE_REQUIRED_BUT_NOT_FOUND = 9;
 
     /**
+     * Indicates that a skip file was successfully written and profile installation will be skipped.
+     */
+    @ResultCode public static final int RESULT_INSTALL_SKIP_FILE_SUCCESS = 10;
+
+    /**
+     * Indicates that a skip file was successfully deleted and profile installation will resume.
+     */
+    @ResultCode public static final int RESULT_DELETE_SKIP_FILE_SUCCESS = 11;
+
+    /**
      * Check if we've already installed a profile for this app installation.
      *
      * @hide
@@ -324,7 +340,11 @@
         return result;
     }
 
-    static void noteProfileWrittenFor(PackageInfo packageInfo, File appFilesDir) {
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    static void noteProfileWrittenFor(@NonNull PackageInfo packageInfo, @NonNull File appFilesDir) {
         File skipFile = new File(appFilesDir, PROFILE_INSTALLER_SKIP_FILE_NAME);
         try (DataOutputStream os = new DataOutputStream(new FileOutputStream(skipFile))) {
             os.writeLong(packageInfo.lastUpdateTime);
@@ -334,6 +354,15 @@
     }
 
     /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    static boolean deleteProfileWrittenFor(@NonNull File appFilesDir) {
+        File skipFile = new File(appFilesDir, PROFILE_INSTALLER_SKIP_FILE_NAME);
+        return skipFile.delete();
+    }
+
+    /**
      * Transcode the source file to an appropriate destination format for this OS version, and
      * write it to the ART aot directory.
      * @param assets the asset manager to read source file from dexopt/baseline.prof
@@ -342,7 +371,6 @@
      * @param filesDir for noting successful installation
      * @param apkName The apk file name the profile is targeting
      * @param diagnostics The diagnostics callback to pass diagnostics to
-     * @return true iff the profile was successfully written
      */
     private static void transcodeAndWrite(
             @NonNull AssetManager assets,
@@ -468,6 +496,7 @@
      *
      */
     @WorkerThread
+    @SuppressWarnings("deprecation")
     static void writeProfile(
             @NonNull Context context,
             @NonNull Executor executor,
@@ -494,4 +523,54 @@
                     diagnostics);
         }
     }
+
+    /**
+     * Writes a profile installation skip file, which makes {@link  ProfileInstaller} skip profile
+     * installation. This is being done so that Macrobenchmarks can request a skip file for
+     * `CompilationMode.None()`, and avoid any interference from {@link  ProfileInstaller}.
+     *
+     * @param context     context to read assets from
+     * @param diagnostics an object which will receive diagnostic information
+     * @param executor    the executor to run the diagnostic events through
+     */
+    @WorkerThread
+    @SuppressWarnings("deprecation")
+    static void writeSkipFile(
+            @NonNull Context context,
+            @NonNull Executor executor,
+            @NonNull DiagnosticsCallback diagnostics
+    ) {
+        Context appContext = context.getApplicationContext();
+        String packageName = appContext.getPackageName();
+        PackageManager packageManager = context.getPackageManager();
+        PackageInfo packageInfo;
+        try {
+            packageInfo = packageManager.getPackageInfo(packageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            result(executor, diagnostics, RESULT_IO_EXCEPTION, e);
+            return;
+        }
+        File filesDir = context.getFilesDir();
+        ProfileInstaller.noteProfileWrittenFor(packageInfo, filesDir);
+        result(executor, diagnostics, RESULT_INSTALL_SKIP_FILE_SUCCESS, null);
+    }
+
+    /**
+     * Deletes a profile installation skip so profile installation can continue after
+     * CompilationMode.None()`.
+     *
+     * @param context     context to read assets from
+     * @param diagnostics an object which will receive diagnostic information
+     * @param executor    the executor to run the diagnostic events through
+     */
+    @WorkerThread
+    static void deleteSkipFile(
+            @NonNull Context context,
+            @NonNull Executor executor,
+            @NonNull DiagnosticsCallback diagnostics
+    ) {
+        File filesDir = context.getFilesDir();
+        ProfileInstaller.deleteProfileWrittenFor(filesDir);
+        result(executor, diagnostics, RESULT_DELETE_SKIP_FILE_SUCCESS, null);
+    }
 }
diff --git a/profileinstaller/profileinstaller/src/test/java/androidx/profileinstaller/ProfileInstallerTest.java b/profileinstaller/profileinstaller/src/test/java/androidx/profileinstaller/ProfileInstallerTest.java
index f04d352..b6b2ca5 100644
--- a/profileinstaller/profileinstaller/src/test/java/androidx/profileinstaller/ProfileInstallerTest.java
+++ b/profileinstaller/profileinstaller/src/test/java/androidx/profileinstaller/ProfileInstallerTest.java
@@ -21,7 +21,6 @@
 import android.content.pm.PackageInfo;
 import android.os.Build;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
@@ -45,7 +44,6 @@
 @RunWith(JUnit4.class)
 public class ProfileInstallerTest extends TestCase {
 
-    @NonNull
     private Path mTmpDir;
 
     @Before
@@ -123,14 +121,37 @@
         packageInfo.lastUpdateTime = 5L;
         TraceDiagnostics diagnosticsCallback = new TraceDiagnostics();
         File appFilesDir = mTmpDir.toFile();
-        boolean result = ProfileInstaller.hasAlreadyWrittenProfileForThisInstall(packageInfo,
-                appFilesDir, diagnosticsCallback);
-
+        ProfileInstaller.hasAlreadyWrittenProfileForThisInstall(
+                packageInfo,
+                appFilesDir,
+                diagnosticsCallback
+        );
         assertThat(diagnosticsCallback.mResults).isEmpty();
     }
 
-    class TraceDiagnostics implements ProfileInstaller.DiagnosticsCallback {
+    @Test
+    public void verifySkipFileDeleted() {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.lastUpdateTime = 5L;
+        TraceDiagnostics diagnosticsCallback = new TraceDiagnostics();
+        File appFilesDir = mTmpDir.toFile();
+        ProfileInstaller.noteProfileWrittenFor(packageInfo, appFilesDir);
+        boolean result = ProfileInstaller.hasAlreadyWrittenProfileForThisInstall(
+                packageInfo,
+                appFilesDir,
+                diagnosticsCallback
+        );
+        assertTrue(result);
+        ProfileInstaller.deleteProfileWrittenFor(appFilesDir);
+        result = ProfileInstaller.hasAlreadyWrittenProfileForThisInstall(
+                packageInfo,
+                appFilesDir,
+                diagnosticsCallback
+        );
+        assertFalse(result);
+    }
 
+    static class TraceDiagnostics implements ProfileInstaller.DiagnosticsCallback {
         List<Integer> mDiagnostics = new ArrayList<>();
         List<Integer> mResults = new ArrayList<>();
 
diff --git a/remotecallback/remotecallback/src/main/java/androidx/remotecallback/CallbackHandlerRegistry.java b/remotecallback/remotecallback/src/main/java/androidx/remotecallback/CallbackHandlerRegistry.java
index d98ae28..bf253c7 100644
--- a/remotecallback/remotecallback/src/main/java/androidx/remotecallback/CallbackHandlerRegistry.java
+++ b/remotecallback/remotecallback/src/main/java/androidx/remotecallback/CallbackHandlerRegistry.java
@@ -72,6 +72,7 @@
         }
     }
 
+    @SuppressWarnings("deprecation")
     private String determineAuthority(Context context, String authority, Class<?> aClass) {
         if (authority != null) {
             return authority;
diff --git a/room/integration-tests/testapp/src/main/AndroidManifest.xml b/room/integration-tests/testapp/src/main/AndroidManifest.xml
index 45c55ab..abd3c54 100644
--- a/room/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/room/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -14,8 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="androidx.room.integration.testapp">
+<manifest
+    package="androidx.room.integration.testapp"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
     <application
         android:name="androidx.multidex.MultiDexApplication"
         android:allowBackup="true"
@@ -24,6 +26,7 @@
             android:name=".SampleDatabaseService"
             android:label="Multi-process SampleDatabase"
             android:exported="false"
-            android:process="androidx.room.integration.testapp.service"/>
+            android:process="androidx.room.integration.testapp.service"
+            tools:ignore="MissingServiceExportedEqualsTrue" />
     </application>
 </manifest>
diff --git a/room/room-compiler-processing/OWNERS b/room/room-compiler-processing/OWNERS
new file mode 100644
index 0000000..23a1553
--- /dev/null
+++ b/room/room-compiler-processing/OWNERS
@@ -0,0 +1 @@
[email protected]
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XEnumEntry.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XEnumEntry.kt
index c271190..c0d53adf 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XEnumEntry.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XEnumEntry.kt
@@ -31,6 +31,15 @@
      * The parent enum type declaration that holds all entries for this enum type..
      */
     override val enclosingElement: XEnumTypeElement
+
+    /**
+     * The parent enum type declaration that holds all entries for this enum type..
+     */
+    @Deprecated(message = "use XEnumEntry#enclosingElement() instead.",
+        replaceWith = ReplaceWith("enclosingElement")
+    )
+    val enumTypeElement: XEnumTypeElement
+        get() = enclosingElement
 }
 
 fun XElement.isEnumEntry(): Boolean {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableParameterElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableParameterElement.kt
index b4b85df..b5148d4 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableParameterElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableParameterElement.kt
@@ -27,6 +27,15 @@
     override val enclosingElement: XExecutableElement
 
     /**
+     * The enclosing [XExecutableElement] this parameter belongs to.
+     */
+    @Deprecated(message = "use XExecutableParameterElement#enclosingElement() instead.",
+        replaceWith = ReplaceWith("enclosingElement")
+    )
+    val enclosingMethodElement: XExecutableElement
+        get() = enclosingElement
+
+    /**
      * `true` if the parameter has a default value, `false` otherwise.
      *
      * Note that when @JvmOverloads is used in a kotlin function with KAPT, only the original
diff --git a/room/room-runtime/src/main/AndroidManifest.xml b/room/room-runtime/src/main/AndroidManifest.xml
index a595d1c..5d8771f 100644
--- a/room/room-runtime/src/main/AndroidManifest.xml
+++ b/room/room-runtime/src/main/AndroidManifest.xml
@@ -14,14 +14,17 @@
   ~ limitations under the License.
   -->
 
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="androidx.room">
+<manifest
+    package="androidx.room"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
 
     <application>
         <service
-                android:name=".MultiInstanceInvalidationService"
-                android:directBootAware="true"
-                android:exported="false"/>
+            android:name=".MultiInstanceInvalidationService"
+            android:directBootAware="true"
+            android:exported="false"
+            tools:ignore="MissingServiceExportedEqualsTrue" />
     </application>
 
 </manifest>
diff --git a/samples/Support4Demos/src/main/java/com/example/android/supportv4/Support4Demos.java b/samples/Support4Demos/src/main/java/com/example/android/supportv4/Support4Demos.java
index 9d9a3ca..5a90e7f 100644
--- a/samples/Support4Demos/src/main/java/com/example/android/supportv4/Support4Demos.java
+++ b/samples/Support4Demos/src/main/java/com/example/android/supportv4/Support4Demos.java
@@ -52,6 +52,7 @@
         getListView().setTextFilterEnabled(true);
     }
 
+    @SuppressWarnings("deprecation")
     protected List<Map<String, Object>> getData(String prefix) {
         List<Map<String, Object>> myData = new ArrayList<Map<String, Object>>();
 
diff --git a/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/PackageValidator.java b/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/PackageValidator.java
index 0c24101..368cd2f 100644
--- a/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/PackageValidator.java
+++ b/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/PackageValidator.java
@@ -90,7 +90,7 @@
     /**
      * @return false if the caller is not authorized to get data from this MediaBrowserService
      */
-    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+    @SuppressWarnings({"BooleanMethodIsAlwaysInverted", "deprecation"})
     public boolean isCallerAllowed(Context context, String callingPackage, int callingUid) {
         // Always allow calls from the framework, self app or development environment.
         if (Process.SYSTEM_UID == callingUid || Process.myUid() == callingUid) {
diff --git a/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/utils/ResourceHelper.java b/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/utils/ResourceHelper.java
index 2083b0c..233e85f 100644
--- a/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/utils/ResourceHelper.java
+++ b/samples/Support4Demos/src/main/java/com/example/android/supportv4/media/utils/ResourceHelper.java
@@ -33,7 +33,7 @@
      * @param defaultColor default to use.
      * @return color value
      */
-    @SuppressWarnings("CatchAndPrintStackTrace")
+    @SuppressWarnings({"CatchAndPrintStackTrace", "deprecation"})
     public static int getThemeColor(Context context, int attribute, int defaultColor) {
         int themeColor = 0;
         String packageName = context.getPackageName();
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/Support7Demos.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/Support7Demos.java
index 3feb3f3..2223ccf 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/Support7Demos.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/Support7Demos.java
@@ -52,6 +52,7 @@
         getListView().setTextFilterEnabled(true);
     }
 
+    @SuppressWarnings("deprecation")
     protected List<Map<String, Object>> getData(String prefix) {
         List<Map<String, Object>> myData = new ArrayList<Map<String, Object>>();
 
diff --git a/samples/SupportAnimationDemos/src/main/java/com/example/android/support/animation/BrowseActivity.java b/samples/SupportAnimationDemos/src/main/java/com/example/android/support/animation/BrowseActivity.java
index 4065625..a44ff7a 100644
--- a/samples/SupportAnimationDemos/src/main/java/com/example/android/support/animation/BrowseActivity.java
+++ b/samples/SupportAnimationDemos/src/main/java/com/example/android/support/animation/BrowseActivity.java
@@ -55,6 +55,7 @@
         getListView().setTextFilterEnabled(true);
     }
 
+    @SuppressWarnings("deprecation")
     protected List<Map<String, Object>> getData(String prefix) {
         List<Map<String, Object>> myData = new ArrayList<Map<String, Object>>();
 
diff --git a/samples/SupportLeanbackDemos/src/main/AndroidManifest.xml b/samples/SupportLeanbackDemos/src/main/AndroidManifest.xml
index ecd3b3e..5838c3f 100644
--- a/samples/SupportLeanbackDemos/src/main/AndroidManifest.xml
+++ b/samples/SupportLeanbackDemos/src/main/AndroidManifest.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="com.example.android.leanback"
     android:versionCode="1"
     android:versionName="1.0">
@@ -204,7 +205,10 @@
             android:name=".MusicExampleActivity"
             android:exported="true"/>
 
-        <service android:exported="false" android:name=".MediaSessionService"/>
+        <service
+            android:exported="false"
+            android:name=".MediaSessionService"
+            tools:ignore="MissingServiceExportedEqualsTrue" />
 
         <activity
             android:name=".DatePickerActivity"
diff --git a/samples/SupportPreferenceDemos/src/main/java/com/example/androidx/preference/MainActivity.java b/samples/SupportPreferenceDemos/src/main/java/com/example/androidx/preference/MainActivity.java
index 05aaf32..f75c81e 100644
--- a/samples/SupportPreferenceDemos/src/main/java/com/example/androidx/preference/MainActivity.java
+++ b/samples/SupportPreferenceDemos/src/main/java/com/example/androidx/preference/MainActivity.java
@@ -58,6 +58,7 @@
     }
 
     @NonNull
+    @SuppressWarnings("deprecation")
     protected List<Map<String, Object>> getActivityList() {
         List<Map<String, Object>> activityList = new ArrayList<>();
 
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceSelectionDialog.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceSelectionDialog.java
index 46c3792..f793a7a 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceSelectionDialog.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceSelectionDialog.java
@@ -148,6 +148,7 @@
         }).start();
     }
 
+    @SuppressWarnings("deprecation")
     private static void showSliceList(Context context, Consumer<Uri> selectedCallback,
             ProviderInfo provider, String label) {
         ProgressDialog dialog = ProgressDialog.show(context, null, "Loading...");
diff --git a/samples/SupportTransitionDemos/src/main/java/com/example/android/support/transition/SupportTransitionDemos.java b/samples/SupportTransitionDemos/src/main/java/com/example/android/support/transition/SupportTransitionDemos.java
index 39b36f5..3d08f1e 100644
--- a/samples/SupportTransitionDemos/src/main/java/com/example/android/support/transition/SupportTransitionDemos.java
+++ b/samples/SupportTransitionDemos/src/main/java/com/example/android/support/transition/SupportTransitionDemos.java
@@ -52,6 +52,7 @@
         getListView().setTextFilterEnabled(true);
     }
 
+    @SuppressWarnings("deprecation")
     protected List<Map<String, Object>> getData(String prefix) {
         List<Map<String, Object>> myData = new ArrayList<>();
 
diff --git a/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorUtilsTest.java b/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorUtilsTest.java
index 22b8025..f03d98f 100644
--- a/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorUtilsTest.java
+++ b/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorUtilsTest.java
@@ -79,6 +79,7 @@
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void getUidForPackage_returnsExpectedUid() throws Exception {
         // The AppAuthenticatorUtils provides an instance method to obtain the UID of the
         // specified package to facilitate tests; this test verifies a base AppAuthenticatorUtils
diff --git a/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticatorUtils.java b/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticatorUtils.java
index 210789f..cdeae4f 100644
--- a/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticatorUtils.java
+++ b/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticatorUtils.java
@@ -70,6 +70,7 @@
      *
      * @see ApplicationInfo#uid
      */
+    @SuppressWarnings("deprecation")
     int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
         ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
         return appInfo.uid;
diff --git a/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppSignatureVerifier.java b/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppSignatureVerifier.java
index 3df7674..29321cc 100644
--- a/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppSignatureVerifier.java
+++ b/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppSignatureVerifier.java
@@ -312,6 +312,7 @@
          * @throws AppSignatureVerifierException if the specified package is not found, or if the
          * {@code SigningInfo} is not returned for the package.
          */
+        @SuppressWarnings("deprecation")
         static AppSigningInfo getAppSigningInfo(PackageManager packageManager,
                 String packageName) throws AppSignatureVerifierException {
             PackageInfo packageInfo;
diff --git a/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppSignatureVerifierTest.java b/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppSignatureVerifierTest.java
index 1854c03..31a1e90 100644
--- a/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppSignatureVerifierTest.java
+++ b/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppSignatureVerifierTest.java
@@ -173,6 +173,7 @@
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void verifySigningIdentity_unknownPackageName() throws Exception {
         // When a package name is specified that is not on the device the #getPackageInfo call
         // should result in a NameNotFoundException; when this is caught the verifier should
@@ -553,6 +554,7 @@
             return this;
         }
 
+        @SuppressWarnings("deprecation")
         private AppSignatureVerifier build() throws Exception {
             if (mCurrentSigners.isEmpty()) {
                 throw new IllegalArgumentException("At least one current signer must be specified");
diff --git a/settings.gradle b/settings.gradle
index 9bf36a2..72bd6a5 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -434,6 +434,7 @@
 includeProject(":compose:ui:ui-test-manifest", "compose/ui/ui-test-manifest", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-test-manifest:integration-tests:testapp", "compose/ui/ui-test-manifest/integration-tests/testapp", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-text", "compose/ui/ui-text", [BuildType.COMPOSE])
+includeProject(":compose:ui:ui-text-google-fonts", "compose/ui/ui-text-google-fonts", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-text:ui-text-benchmark", "compose/ui/ui-text/benchmark", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-text:ui-text-samples", "compose/ui/ui-text/samples", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-tooling", "compose/ui/ui-tooling", [BuildType.COMPOSE])
diff --git a/sharetarget/sharetarget/src/main/java/androidx/sharetarget/ShareTargetXmlParser.java b/sharetarget/sharetarget/src/main/java/androidx/sharetarget/ShareTargetXmlParser.java
index ed99123..7ff445d 100644
--- a/sharetarget/sharetarget/src/main/java/androidx/sharetarget/ShareTargetXmlParser.java
+++ b/sharetarget/sharetarget/src/main/java/androidx/sharetarget/ShareTargetXmlParser.java
@@ -82,6 +82,7 @@
         /* Hide the constructor */
     }
 
+    @SuppressWarnings("deprecation")
     private static ArrayList<ShareTargetCompat> parseShareTargets(Context context) {
         ArrayList<ShareTargetCompat> targets = new ArrayList<>();
 
diff --git a/slice/slice-core/src/main/java/androidx/slice/SliceProvider.java b/slice/slice-core/src/main/java/androidx/slice/SliceProvider.java
index 68a80dc..8a72034 100644
--- a/slice/slice-core/src/main/java/androidx/slice/SliceProvider.java
+++ b/slice/slice-core/src/main/java/androidx/slice/SliceProvider.java
@@ -384,6 +384,7 @@
      * Get string describing permission request.
      */
     @RequiresApi(19)
+    @SuppressWarnings("deprecation")
     private static CharSequence getPermissionString(Context context, String callingPackage) {
         PackageManager pm = context.getPackageManager();
         try {
diff --git a/slice/slice-core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java b/slice/slice-core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java
index bfd3368..a659a18 100644
--- a/slice/slice-core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java
+++ b/slice/slice-core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java
@@ -56,6 +56,7 @@
     private AlertDialog mDialog;
 
     @Override
+    @SuppressWarnings("deprecation")
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
diff --git a/slice/slice-core/src/main/java/androidx/slice/compat/SliceProviderCompat.java b/slice/slice-core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
index 6da7d06..547c821 100644
--- a/slice/slice-core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
+++ b/slice/slice-core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
@@ -377,6 +377,7 @@
      * Compat version of {@link Slice#bindSlice}.
      */
     @Nullable
+    @SuppressWarnings("deprecation")
     public static Slice bindSlice(@NonNull Context context, @NonNull Intent intent,
             @NonNull Set<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(intent, "intent");
@@ -540,6 +541,7 @@
      * Compat version of {@link android.app.slice.SliceManager#mapIntentToUri}.
      */
     @Nullable
+    @SuppressWarnings("deprecation")
     public static Uri mapIntentToUri(@NonNull Context context, @NonNull Intent intent) {
         Preconditions.checkNotNull(intent, "intent");
         Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
diff --git a/slice/slice-view/src/androidTest/java/androidx/slice/SliceViewManagerTest.java b/slice/slice-view/src/androidTest/java/androidx/slice/SliceViewManagerTest.java
index ca207d4..c276a95 100644
--- a/slice/slice-view/src/androidTest/java/androidx/slice/SliceViewManagerTest.java
+++ b/slice/slice-view/src/androidTest/java/androidx/slice/SliceViewManagerTest.java
@@ -210,6 +210,7 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 28)
+    @SuppressWarnings("deprecation")
     public void testSuspended() throws PackageManager.NameNotFoundException {
         Uri uri = new Uri.Builder()
                 .scheme(ContentResolver.SCHEME_CONTENT)
diff --git a/slice/slice-view/src/main/java/androidx/slice/SliceViewManagerWrapper.java b/slice/slice-view/src/main/java/androidx/slice/SliceViewManagerWrapper.java
index 1367f4d..5c9989d 100644
--- a/slice/slice-view/src/main/java/androidx/slice/SliceViewManagerWrapper.java
+++ b/slice/slice-view/src/main/java/androidx/slice/SliceViewManagerWrapper.java
@@ -134,6 +134,7 @@
         return isPackageSuspended(pkg);
     }
 
+    @SuppressWarnings("deprecation")
     private boolean isPackageSuspended(String pkg) {
         Boolean isSuspended = mCachedSuspendFlags.get(pkg);
         if (isSuspended == null) {
diff --git a/startup/startup-runtime/src/main/java/androidx/startup/AppInitializer.java b/startup/startup-runtime/src/main/java/androidx/startup/AppInitializer.java
index 1a85102..db4dc87 100644
--- a/startup/startup-runtime/src/main/java/androidx/startup/AppInitializer.java
+++ b/startup/startup-runtime/src/main/java/androidx/startup/AppInitializer.java
@@ -195,6 +195,7 @@
         }
     }
 
+    @SuppressWarnings("deprecation")
     void discoverAndInitialize() {
         try {
             Trace.beginSection(SECTION_NAME);
diff --git a/template/template-appwidget/integration-tests/demos/src/main/java/androidx/template/appwidget/demos/TemplateInputActivity.kt b/template/template-appwidget/integration-tests/demos/src/main/java/androidx/template/appwidget/demos/TemplateInputActivity.kt
index a1e817d..a3a8398 100644
--- a/template/template-appwidget/integration-tests/demos/src/main/java/androidx/template/appwidget/demos/TemplateInputActivity.kt
+++ b/template/template-appwidget/integration-tests/demos/src/main/java/androidx/template/appwidget/demos/TemplateInputActivity.kt
@@ -71,6 +71,7 @@
 import androidx.template.template.GlanceTemplate
 import androidx.template.template.TemplateImageWithDescription
 import androidx.template.template.SingleEntityTemplate
+import androidx.template.template.TemplateText
 import androidx.template.template.TemplateTextButton
 import androidx.template.template.TemplateImageButton
 import kotlinx.coroutines.CoroutineScope
@@ -390,16 +391,13 @@
     body: String,
     background: ColorProvider
 ) = SingleEntityTemplate.Data(
-    header = "Single Entity Example",
-    headerIcon = TemplateImageWithDescription(
-        ImageProvider(R.drawable.compose),
-        "Header icon"
-    ),
-    title = title,
-    subtitle = subtitle,
-    bodyText = body,
+    header = TemplateText("Template demo"),
+    headerIcon = TemplateImageWithDescription(ImageProvider(R.drawable.compose), "Header icon"),
+    title = TemplateText(title, 1),
+    subtitle = TemplateText(subtitle, 2),
+    body = TemplateText(body, 3),
     button = TemplateTextButton(actionRunCallback<TemplateButtonAction>(), "Apply"),
-    mainImage = TemplateImageWithDescription(ImageProvider(R.drawable.compose), "Compose image"),
+    image = TemplateImageWithDescription(ImageProvider(R.drawable.compose), "Compose image"),
     backgroundColor = background
 )
 
@@ -409,13 +407,10 @@
     background: ColorProvider,
     backgroundImage: ImageProvider?
 ) = FreeformTemplate.Data(
-    header = "Freeform Example",
-    headerIcon = TemplateImageWithDescription(
-        ImageProvider(R.drawable.compose),
-        "Header icon"
-    ),
-    title = title,
-    subtitle = subtitle,
+    header = TemplateText("Freeform Example"),
+    headerIcon = TemplateImageWithDescription(ImageProvider(R.drawable.compose), "Header icon"),
+    title = TemplateText(title),
+    subtitle = TemplateText(subtitle),
     actionIcon = TemplateImageButton(
         actionRunCallback<TemplateButtonAction>(),
         TemplateImageWithDescription(ImageProvider(R.drawable.ic_favorite), "Apply")
diff --git a/template/template/api/current.txt b/template/template/api/current.txt
index 2c16bc5..eab29e6 100644
--- a/template/template/api/current.txt
+++ b/template/template/api/current.txt
@@ -9,21 +9,21 @@
   }
 
   public static final class FreeformTemplate.Data {
-    ctor public FreeformTemplate.Data(androidx.glance.unit.ColorProvider backgroundColor, androidx.template.template.TemplateImageWithDescription headerIcon, androidx.template.template.TemplateImageButton? actionIcon, optional String? header, optional String? title, optional String? subtitle, optional androidx.glance.ImageProvider? backgroundImage);
+    ctor public FreeformTemplate.Data(androidx.glance.unit.ColorProvider backgroundColor, androidx.template.template.TemplateImageWithDescription headerIcon, androidx.template.template.TemplateImageButton? actionIcon, optional androidx.template.template.TemplateText? header, optional androidx.template.template.TemplateText? title, optional androidx.template.template.TemplateText? subtitle, optional androidx.glance.ImageProvider? backgroundImage);
     method public androidx.template.template.TemplateImageButton? getActionIcon();
     method public androidx.glance.unit.ColorProvider getBackgroundColor();
     method public androidx.glance.ImageProvider? getBackgroundImage();
-    method public String? getHeader();
+    method public androidx.template.template.TemplateText? getHeader();
     method public androidx.template.template.TemplateImageWithDescription getHeaderIcon();
-    method public String? getSubtitle();
-    method public String? getTitle();
+    method public androidx.template.template.TemplateText? getSubtitle();
+    method public androidx.template.template.TemplateText? getTitle();
     property public final androidx.template.template.TemplateImageButton? actionIcon;
     property public final androidx.glance.unit.ColorProvider backgroundColor;
     property public final androidx.glance.ImageProvider? backgroundImage;
-    property public final String? header;
+    property public final androidx.template.template.TemplateText? header;
     property public final androidx.template.template.TemplateImageWithDescription headerIcon;
-    property public final String? subtitle;
-    property public final String? title;
+    property public final androidx.template.template.TemplateText? subtitle;
+    property public final androidx.template.template.TemplateText? title;
   }
 
   public abstract class GlanceTemplate<T> {
@@ -45,23 +45,23 @@
   }
 
   public static final class SingleEntityTemplate.Data {
-    ctor public SingleEntityTemplate.Data(androidx.glance.unit.ColorProvider backgroundColor, androidx.template.template.TemplateImageWithDescription headerIcon, optional String? header, optional String? title, optional String? subtitle, optional String? bodyText, optional androidx.template.template.TemplateButton? button, optional androidx.template.template.TemplateImageWithDescription? mainImage);
-    method public androidx.glance.unit.ColorProvider getBackgroundColor();
-    method public String? getBodyText();
+    ctor public SingleEntityTemplate.Data(androidx.template.template.TemplateImageWithDescription headerIcon, optional androidx.template.template.TemplateText? header, optional androidx.template.template.TemplateText? title, optional androidx.template.template.TemplateText? subtitle, optional androidx.template.template.TemplateText? body, optional androidx.template.template.TemplateButton? button, optional androidx.template.template.TemplateImageWithDescription? image, optional androidx.glance.unit.ColorProvider? backgroundColor);
+    method public androidx.glance.unit.ColorProvider? getBackgroundColor();
+    method public androidx.template.template.TemplateText? getBody();
     method public androidx.template.template.TemplateButton? getButton();
-    method public String? getHeader();
+    method public androidx.template.template.TemplateText? getHeader();
     method public androidx.template.template.TemplateImageWithDescription getHeaderIcon();
-    method public androidx.template.template.TemplateImageWithDescription? getMainImage();
-    method public String? getSubtitle();
-    method public String? getTitle();
-    property public final androidx.glance.unit.ColorProvider backgroundColor;
-    property public final String? bodyText;
+    method public androidx.template.template.TemplateImageWithDescription? getImage();
+    method public androidx.template.template.TemplateText? getSubtitle();
+    method public androidx.template.template.TemplateText? getTitle();
+    property public final androidx.glance.unit.ColorProvider? backgroundColor;
+    property public final androidx.template.template.TemplateText? body;
     property public final androidx.template.template.TemplateButton? button;
-    property public final String? header;
+    property public final androidx.template.template.TemplateText? header;
     property public final androidx.template.template.TemplateImageWithDescription headerIcon;
-    property public final androidx.template.template.TemplateImageWithDescription? mainImage;
-    property public final String? subtitle;
-    property public final String? title;
+    property public final androidx.template.template.TemplateImageWithDescription? image;
+    property public final androidx.template.template.TemplateText? subtitle;
+    property public final androidx.template.template.TemplateText? title;
   }
 
   public abstract sealed class TemplateButton {
@@ -83,6 +83,16 @@
     property public final androidx.glance.ImageProvider image;
   }
 
+  public final class TemplateText {
+    ctor public TemplateText(String text, optional int priority, optional androidx.glance.unit.ColorProvider? color);
+    method public androidx.glance.unit.ColorProvider? getColor();
+    method public int getPriority();
+    method public String getText();
+    property public final androidx.glance.unit.ColorProvider? color;
+    property public final int priority;
+    property public final String text;
+  }
+
   public final class TemplateTextButton extends androidx.template.template.TemplateButton {
     ctor public TemplateTextButton(androidx.glance.action.Action action, String text);
     method public String getText();
diff --git a/template/template/api/public_plus_experimental_current.txt b/template/template/api/public_plus_experimental_current.txt
index 2c16bc5..eab29e6 100644
--- a/template/template/api/public_plus_experimental_current.txt
+++ b/template/template/api/public_plus_experimental_current.txt
@@ -9,21 +9,21 @@
   }
 
   public static final class FreeformTemplate.Data {
-    ctor public FreeformTemplate.Data(androidx.glance.unit.ColorProvider backgroundColor, androidx.template.template.TemplateImageWithDescription headerIcon, androidx.template.template.TemplateImageButton? actionIcon, optional String? header, optional String? title, optional String? subtitle, optional androidx.glance.ImageProvider? backgroundImage);
+    ctor public FreeformTemplate.Data(androidx.glance.unit.ColorProvider backgroundColor, androidx.template.template.TemplateImageWithDescription headerIcon, androidx.template.template.TemplateImageButton? actionIcon, optional androidx.template.template.TemplateText? header, optional androidx.template.template.TemplateText? title, optional androidx.template.template.TemplateText? subtitle, optional androidx.glance.ImageProvider? backgroundImage);
     method public androidx.template.template.TemplateImageButton? getActionIcon();
     method public androidx.glance.unit.ColorProvider getBackgroundColor();
     method public androidx.glance.ImageProvider? getBackgroundImage();
-    method public String? getHeader();
+    method public androidx.template.template.TemplateText? getHeader();
     method public androidx.template.template.TemplateImageWithDescription getHeaderIcon();
-    method public String? getSubtitle();
-    method public String? getTitle();
+    method public androidx.template.template.TemplateText? getSubtitle();
+    method public androidx.template.template.TemplateText? getTitle();
     property public final androidx.template.template.TemplateImageButton? actionIcon;
     property public final androidx.glance.unit.ColorProvider backgroundColor;
     property public final androidx.glance.ImageProvider? backgroundImage;
-    property public final String? header;
+    property public final androidx.template.template.TemplateText? header;
     property public final androidx.template.template.TemplateImageWithDescription headerIcon;
-    property public final String? subtitle;
-    property public final String? title;
+    property public final androidx.template.template.TemplateText? subtitle;
+    property public final androidx.template.template.TemplateText? title;
   }
 
   public abstract class GlanceTemplate<T> {
@@ -45,23 +45,23 @@
   }
 
   public static final class SingleEntityTemplate.Data {
-    ctor public SingleEntityTemplate.Data(androidx.glance.unit.ColorProvider backgroundColor, androidx.template.template.TemplateImageWithDescription headerIcon, optional String? header, optional String? title, optional String? subtitle, optional String? bodyText, optional androidx.template.template.TemplateButton? button, optional androidx.template.template.TemplateImageWithDescription? mainImage);
-    method public androidx.glance.unit.ColorProvider getBackgroundColor();
-    method public String? getBodyText();
+    ctor public SingleEntityTemplate.Data(androidx.template.template.TemplateImageWithDescription headerIcon, optional androidx.template.template.TemplateText? header, optional androidx.template.template.TemplateText? title, optional androidx.template.template.TemplateText? subtitle, optional androidx.template.template.TemplateText? body, optional androidx.template.template.TemplateButton? button, optional androidx.template.template.TemplateImageWithDescription? image, optional androidx.glance.unit.ColorProvider? backgroundColor);
+    method public androidx.glance.unit.ColorProvider? getBackgroundColor();
+    method public androidx.template.template.TemplateText? getBody();
     method public androidx.template.template.TemplateButton? getButton();
-    method public String? getHeader();
+    method public androidx.template.template.TemplateText? getHeader();
     method public androidx.template.template.TemplateImageWithDescription getHeaderIcon();
-    method public androidx.template.template.TemplateImageWithDescription? getMainImage();
-    method public String? getSubtitle();
-    method public String? getTitle();
-    property public final androidx.glance.unit.ColorProvider backgroundColor;
-    property public final String? bodyText;
+    method public androidx.template.template.TemplateImageWithDescription? getImage();
+    method public androidx.template.template.TemplateText? getSubtitle();
+    method public androidx.template.template.TemplateText? getTitle();
+    property public final androidx.glance.unit.ColorProvider? backgroundColor;
+    property public final androidx.template.template.TemplateText? body;
     property public final androidx.template.template.TemplateButton? button;
-    property public final String? header;
+    property public final androidx.template.template.TemplateText? header;
     property public final androidx.template.template.TemplateImageWithDescription headerIcon;
-    property public final androidx.template.template.TemplateImageWithDescription? mainImage;
-    property public final String? subtitle;
-    property public final String? title;
+    property public final androidx.template.template.TemplateImageWithDescription? image;
+    property public final androidx.template.template.TemplateText? subtitle;
+    property public final androidx.template.template.TemplateText? title;
   }
 
   public abstract sealed class TemplateButton {
@@ -83,6 +83,16 @@
     property public final androidx.glance.ImageProvider image;
   }
 
+  public final class TemplateText {
+    ctor public TemplateText(String text, optional int priority, optional androidx.glance.unit.ColorProvider? color);
+    method public androidx.glance.unit.ColorProvider? getColor();
+    method public int getPriority();
+    method public String getText();
+    property public final androidx.glance.unit.ColorProvider? color;
+    property public final int priority;
+    property public final String text;
+  }
+
   public final class TemplateTextButton extends androidx.template.template.TemplateButton {
     ctor public TemplateTextButton(androidx.glance.action.Action action, String text);
     method public String getText();
diff --git a/template/template/api/restricted_current.txt b/template/template/api/restricted_current.txt
index 2c16bc5..eab29e6 100644
--- a/template/template/api/restricted_current.txt
+++ b/template/template/api/restricted_current.txt
@@ -9,21 +9,21 @@
   }
 
   public static final class FreeformTemplate.Data {
-    ctor public FreeformTemplate.Data(androidx.glance.unit.ColorProvider backgroundColor, androidx.template.template.TemplateImageWithDescription headerIcon, androidx.template.template.TemplateImageButton? actionIcon, optional String? header, optional String? title, optional String? subtitle, optional androidx.glance.ImageProvider? backgroundImage);
+    ctor public FreeformTemplate.Data(androidx.glance.unit.ColorProvider backgroundColor, androidx.template.template.TemplateImageWithDescription headerIcon, androidx.template.template.TemplateImageButton? actionIcon, optional androidx.template.template.TemplateText? header, optional androidx.template.template.TemplateText? title, optional androidx.template.template.TemplateText? subtitle, optional androidx.glance.ImageProvider? backgroundImage);
     method public androidx.template.template.TemplateImageButton? getActionIcon();
     method public androidx.glance.unit.ColorProvider getBackgroundColor();
     method public androidx.glance.ImageProvider? getBackgroundImage();
-    method public String? getHeader();
+    method public androidx.template.template.TemplateText? getHeader();
     method public androidx.template.template.TemplateImageWithDescription getHeaderIcon();
-    method public String? getSubtitle();
-    method public String? getTitle();
+    method public androidx.template.template.TemplateText? getSubtitle();
+    method public androidx.template.template.TemplateText? getTitle();
     property public final androidx.template.template.TemplateImageButton? actionIcon;
     property public final androidx.glance.unit.ColorProvider backgroundColor;
     property public final androidx.glance.ImageProvider? backgroundImage;
-    property public final String? header;
+    property public final androidx.template.template.TemplateText? header;
     property public final androidx.template.template.TemplateImageWithDescription headerIcon;
-    property public final String? subtitle;
-    property public final String? title;
+    property public final androidx.template.template.TemplateText? subtitle;
+    property public final androidx.template.template.TemplateText? title;
   }
 
   public abstract class GlanceTemplate<T> {
@@ -45,23 +45,23 @@
   }
 
   public static final class SingleEntityTemplate.Data {
-    ctor public SingleEntityTemplate.Data(androidx.glance.unit.ColorProvider backgroundColor, androidx.template.template.TemplateImageWithDescription headerIcon, optional String? header, optional String? title, optional String? subtitle, optional String? bodyText, optional androidx.template.template.TemplateButton? button, optional androidx.template.template.TemplateImageWithDescription? mainImage);
-    method public androidx.glance.unit.ColorProvider getBackgroundColor();
-    method public String? getBodyText();
+    ctor public SingleEntityTemplate.Data(androidx.template.template.TemplateImageWithDescription headerIcon, optional androidx.template.template.TemplateText? header, optional androidx.template.template.TemplateText? title, optional androidx.template.template.TemplateText? subtitle, optional androidx.template.template.TemplateText? body, optional androidx.template.template.TemplateButton? button, optional androidx.template.template.TemplateImageWithDescription? image, optional androidx.glance.unit.ColorProvider? backgroundColor);
+    method public androidx.glance.unit.ColorProvider? getBackgroundColor();
+    method public androidx.template.template.TemplateText? getBody();
     method public androidx.template.template.TemplateButton? getButton();
-    method public String? getHeader();
+    method public androidx.template.template.TemplateText? getHeader();
     method public androidx.template.template.TemplateImageWithDescription getHeaderIcon();
-    method public androidx.template.template.TemplateImageWithDescription? getMainImage();
-    method public String? getSubtitle();
-    method public String? getTitle();
-    property public final androidx.glance.unit.ColorProvider backgroundColor;
-    property public final String? bodyText;
+    method public androidx.template.template.TemplateImageWithDescription? getImage();
+    method public androidx.template.template.TemplateText? getSubtitle();
+    method public androidx.template.template.TemplateText? getTitle();
+    property public final androidx.glance.unit.ColorProvider? backgroundColor;
+    property public final androidx.template.template.TemplateText? body;
     property public final androidx.template.template.TemplateButton? button;
-    property public final String? header;
+    property public final androidx.template.template.TemplateText? header;
     property public final androidx.template.template.TemplateImageWithDescription headerIcon;
-    property public final androidx.template.template.TemplateImageWithDescription? mainImage;
-    property public final String? subtitle;
-    property public final String? title;
+    property public final androidx.template.template.TemplateImageWithDescription? image;
+    property public final androidx.template.template.TemplateText? subtitle;
+    property public final androidx.template.template.TemplateText? title;
   }
 
   public abstract sealed class TemplateButton {
@@ -83,6 +83,16 @@
     property public final androidx.glance.ImageProvider image;
   }
 
+  public final class TemplateText {
+    ctor public TemplateText(String text, optional int priority, optional androidx.glance.unit.ColorProvider? color);
+    method public androidx.glance.unit.ColorProvider? getColor();
+    method public int getPriority();
+    method public String getText();
+    property public final androidx.glance.unit.ColorProvider? color;
+    property public final int priority;
+    property public final String text;
+  }
+
   public final class TemplateTextButton extends androidx.template.template.TemplateButton {
     ctor public TemplateTextButton(androidx.glance.action.Action action, String text);
     method public String getText();
diff --git a/template/template/src/main/java/androidx/template/template/FreeformTemplate.kt b/template/template/src/main/java/androidx/template/template/FreeformTemplate.kt
index 223137c..3c821cc 100644
--- a/template/template/src/main/java/androidx/template/template/FreeformTemplate.kt
+++ b/template/template/src/main/java/androidx/template/template/FreeformTemplate.kt
@@ -50,8 +50,8 @@
                 horizontalAlignment = Alignment.CenterHorizontally
             ) {
                 // TODO: Text block ordering
-                it.title?.let { title -> Title(title) }
-                it.subtitle?.let { subtitle -> Subtitle(subtitle) }
+                it.title?.let { title -> Title(title.text) }
+                it.subtitle?.let { subtitle -> Subtitle(subtitle.text) }
             }
             Action(it.actionIcon)
         }
@@ -65,8 +65,8 @@
                 horizontalAlignment = Alignment.CenterHorizontally
             ) {
                 TemplateHeader(it.headerIcon, it.header)
-                it.title?.let { title -> Title(title) }
-                it.subtitle?.let { subtitle -> Subtitle(subtitle) }
+                it.title?.let { title -> Title(title.text) }
+                it.subtitle?.let { subtitle -> Subtitle(subtitle.text) }
             }
             Action(it.actionIcon)
         }
@@ -80,8 +80,8 @@
                 horizontalAlignment = Alignment.CenterHorizontally
             ) {
                 TemplateHeader(it.headerIcon, it.header)
-                it.title?.let { title -> Title(title) }
-                it.subtitle?.let { subtitle -> Subtitle(subtitle) }
+                it.title?.let { title -> Title(title.text) }
+                it.subtitle?.let { subtitle -> Subtitle(subtitle.text) }
             }
             Action(it.actionIcon)
         }
@@ -138,17 +138,17 @@
      * @param headerIcon Logo icon, displayed in the glanceable header
      * @param actionIcon Action icon button
      * @param header Main header text
-     * @param title Text section main title
-     * @param subtitle Text section subtitle
+     * @param title Text section main title, priority ordered
+     * @param subtitle Text section subtitle, priority ordered
      * @param backgroundImage Background image, if set replaces the glanceable background
      */
     class Data(
         val backgroundColor: ColorProvider,
         val headerIcon: TemplateImageWithDescription,
         val actionIcon: TemplateImageButton?,
-        val header: String? = null,
-        val title: String? = null,
-        val subtitle: String? = null,
+        val header: TemplateText? = null,
+        val title: TemplateText? = null,
+        val subtitle: TemplateText? = null,
         val backgroundImage: ImageProvider? = null,
     ) {
 
diff --git a/template/template/src/main/java/androidx/template/template/GlanceTemplate.kt b/template/template/src/main/java/androidx/template/template/GlanceTemplate.kt
index cd74697..ef4ad09 100644
--- a/template/template/src/main/java/androidx/template/template/GlanceTemplate.kt
+++ b/template/template/src/main/java/androidx/template/template/GlanceTemplate.kt
@@ -17,7 +17,6 @@
 package androidx.template.template
 
 import androidx.compose.runtime.Composable
-import androidx.glance.ImageProvider
 import androidx.glance.action.Action
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
@@ -26,16 +25,25 @@
 import androidx.glance.layout.Alignment
 import androidx.glance.layout.Row
 import androidx.glance.layout.Spacer
-import androidx.glance.layout.fillMaxWidth
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.TextUnit
+import androidx.glance.Button
+import androidx.glance.ImageProvider
+import androidx.glance.LocalSize
+import androidx.glance.action.clickable
+import androidx.glance.background
+import androidx.glance.layout.Column
 import androidx.glance.layout.height
 import androidx.glance.layout.width
 import androidx.glance.text.Text
 import androidx.glance.text.TextStyle
+import androidx.glance.unit.ColorProvider
 
 /** Transforms semantic data into a composable layout for any glanceable */
 abstract class GlanceTemplate<T> {
 
-    /** Defines the data associated with this template */
+    /** Defines the data associated with this template. */
     abstract fun getData(state: Any?): T
 
     /** Default layout implementation for AppWidget glanceable, at "collapsed" display size. */
@@ -54,12 +62,46 @@
 }
 
 /**
+ * Contains the information required to display a string on a template.
+ *
+ * @param text string to be displayed
+ * @param priority the priority order for this string, in orderable blocks
+ * @param color text color
+ */
+public class TemplateText(
+    val text: String,
+    val priority: Int = 0,
+    val color: ColorProvider? = null
+) {
+
+    override fun hashCode(): Int {
+        var result = text.hashCode()
+        result = 31 * result + priority
+        result = 31 * result + (color?.hashCode() ?: 0)
+        return result
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as TemplateText
+
+        if (text != other.text) return false
+        if (priority != other.priority) return false
+        if (color != other.color) return false
+
+        return true
+    }
+}
+
+/**
  * Contains the information required to display an image on a template.
  *
  * @param image The image to display
  * @param description The image description, usually used as alt text
  */
-class TemplateImageWithDescription(val image: ImageProvider, val description: String) {
+public class TemplateImageWithDescription(val image: ImageProvider, val description: String) {
 
     override fun hashCode(): Int = 31 * image.hashCode() + description.hashCode()
 
@@ -77,7 +119,7 @@
  *
  * @param action The onClick action
  */
-sealed class TemplateButton(val action: Action) {
+public sealed class TemplateButton(val action: Action) {
 
     override fun hashCode(): Int = action.hashCode()
 
@@ -95,7 +137,7 @@
  * @param action The onClick action
  * @param text The button display text
  */
-class TemplateTextButton(action: Action, val text: String) : TemplateButton(action) {
+public class TemplateTextButton(action: Action, val text: String) : TemplateButton(action) {
 
     override fun hashCode(): Int = 31 * super.hashCode() + text.hashCode()
 
@@ -114,7 +156,7 @@
  * @param action The onClick action
  * @param image The button image
  */
-class TemplateImageButton(
+public class TemplateImageButton(
     action: Action,
     val image: TemplateImageWithDescription
 ) : TemplateButton(action) {
@@ -140,10 +182,10 @@
 @Composable
 internal fun TemplateHeader(
     headerIcon: TemplateImageWithDescription,
-    header: String? = null
+    header: TemplateText?
 ) {
     Row(
-        modifier = GlanceModifier.fillMaxWidth(),
+        modifier = GlanceModifier.background(Color.Transparent),
         verticalAlignment = Alignment.CenterVertically
     ) {
         Image(
@@ -153,12 +195,132 @@
         )
         header?.let {
             Spacer(modifier = GlanceModifier.width(8.dp))
-            // TODO: Text color customization
+            val size =
+                textSize(TemplateTextType.Title, DisplaySize.fromDpSize(LocalSize.current))
+
             Text(
-                text = header,
-                style = TextStyle(fontSize = 20.sp),
+                modifier = GlanceModifier.defaultWeight(),
+                text = header.text,
+                style = TextStyle(fontSize = size, color = it.color),
                 maxLines = 1
             )
         }
     }
 }
+
+/**
+ * Default glanceable text section layout. Displays a list of reorderable text fields, ordered by
+ * priority and styled according to the [TemplateTextType] of each field.
+ *
+ * @param textList the list of text fields to display in the block
+ */
+@Composable
+internal fun TextSection(textList: List<TypedTemplateText>) {
+    if (textList.isEmpty()) return
+
+    val sorted = textList.sortedBy { it.text.priority }
+    Column(modifier = GlanceModifier.background(Color.Transparent)) {
+        sorted.forEach {
+            // TODO: Spacing
+            val size = textSize(it.textType, DisplaySize.fromDpSize(LocalSize.current))
+            Text(
+                it.text.text,
+                style = TextStyle(fontSize = size, color = it.text.color),
+                maxLines = maxLines(it.textType),
+                modifier = GlanceModifier.background(Color.Transparent)
+            )
+        }
+    }
+}
+
+/**
+ * Displays a [TemplateButton]
+ */
+@Composable
+internal fun TemplateButton(button: TemplateButton) {
+    when (button) {
+        is TemplateImageButton -> {
+            // TODO: Specify sizing for image button
+            val image = button.image
+            Image(
+                provider = image.image,
+                contentDescription = image.description,
+                modifier = GlanceModifier.clickable(button.action)
+            )
+        }
+        is TemplateTextButton -> {
+            Button(text = button.text, onClick = button.action)
+        }
+    }
+}
+
+/**
+ * A [TemplateText] tagged with the [TemplateTextType] of the field. Templates can use the
+ * text type to assign appropriate styling.
+ *
+ * @param text base text field
+ * @param textType text field type type
+ */
+internal data class TypedTemplateText(val text: TemplateText, val textType: TemplateTextType)
+
+/**
+ * The text types that can be used with templates
+ */
+internal enum class TemplateTextType {
+    Display,
+    Title,
+    Label,
+    Body
+}
+
+private enum class DisplaySize {
+    Small,
+    Medium,
+    Large;
+
+    companion object {
+        fun fromDpSize(dpSize: DpSize): DisplaySize =
+            if (dpSize.width < 180.dp && dpSize.height < 120.dp) {
+                Small
+            } else if (dpSize.width < 280.dp && dpSize.height < 180.dp) {
+                Medium
+            } else {
+                Large
+            }
+    }
+}
+
+private fun textSize(textClass: TemplateTextType, displaySize: DisplaySize): TextUnit =
+    when (textClass) {
+        // TODO: Does display scale?
+        TemplateTextType.Display -> 45.sp
+        TemplateTextType.Title -> {
+            when (displaySize) {
+                DisplaySize.Small -> 14.sp
+                DisplaySize.Medium -> 16.sp
+                DisplaySize.Large -> 22.sp
+            }
+        }
+        TemplateTextType.Body -> {
+            when (displaySize) {
+                DisplaySize.Small -> 12.sp
+                DisplaySize.Medium -> 14.sp
+                DisplaySize.Large -> 14.sp
+            }
+        }
+        TemplateTextType.Label -> {
+            when (displaySize) {
+                DisplaySize.Small -> 11.sp
+                DisplaySize.Medium -> 12.sp
+                DisplaySize.Large -> 14.sp
+            }
+        }
+    }
+
+private fun maxLines(textClass: TemplateTextType): Int =
+    when (textClass) {
+        TemplateTextType.Display -> 1
+        TemplateTextType.Title -> 3
+        TemplateTextType.Body -> 3
+        TemplateTextType.Label -> 1
+    }
diff --git a/template/template/src/main/java/androidx/template/template/SingleEntityTemplate.kt b/template/template/src/main/java/androidx/template/template/SingleEntityTemplate.kt
index a210b9d..5fb4e90 100644
--- a/template/template/src/main/java/androidx/template/template/SingleEntityTemplate.kt
+++ b/template/template/src/main/java/androidx/template/template/SingleEntityTemplate.kt
@@ -16,23 +16,24 @@
 
 package androidx.template.template
 
-import android.util.Log
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.glance.Button
 import androidx.glance.GlanceModifier
 import androidx.glance.Image
-import androidx.glance.action.clickable
+import androidx.glance.LocalSize
 import androidx.glance.background
 import androidx.glance.currentState
 import androidx.glance.layout.Column
+import androidx.glance.layout.ContentScale
 import androidx.glance.layout.Row
+import androidx.glance.layout.Spacer
+import androidx.glance.layout.fillMaxHeight
 import androidx.glance.layout.fillMaxSize
 import androidx.glance.layout.fillMaxWidth
+import androidx.glance.layout.height
 import androidx.glance.layout.padding
-import androidx.glance.text.Text
-import androidx.glance.text.TextStyle
+import androidx.glance.layout.width
 import androidx.glance.unit.ColorProvider
 
 /**
@@ -43,12 +44,15 @@
     @Composable
     override fun WidgetLayoutCollapsed() {
         getData(currentState()).let {
-            Column(
-                modifier = GlanceModifier.fillMaxSize().padding(8.dp).background(it.backgroundColor)
-            ) {
+            var modifier = GlanceModifier.fillMaxSize().padding(16.dp)
+            it.backgroundColor?.let { color -> modifier = modifier.background(color) }
+            it.image?.let { image ->
+                modifier = modifier.background(image.image, ContentScale.Crop)
+            }
+            Column(modifier = modifier) {
                 TemplateHeader(it.headerIcon, it.header)
-                TextBlock(it.title, it.subtitle, null)
-                Button(it)
+                Spacer(modifier = GlanceModifier.defaultWeight())
+                TextSection(textList(it.title, it.subtitle))
             }
         }
     }
@@ -56,15 +60,25 @@
     @Composable
     override fun WidgetLayoutVertical() {
         getData(currentState()).let {
-            Column(
-                modifier = GlanceModifier.fillMaxSize().padding(8.dp).background(it.backgroundColor)
-            ) {
+            var modifier = GlanceModifier.fillMaxSize().padding(16.dp)
+            it.backgroundColor?.let { color -> modifier = modifier.background(color) }
+            Column(modifier = modifier) {
                 TemplateHeader(it.headerIcon, it.header)
-                TextBlock(it.title, it.subtitle, it.bodyText)
-                it.mainImage?.let { image ->
-                    Image(provider = image.image, contentDescription = image.description)
+                Spacer(modifier = GlanceModifier.height(16.dp))
+                it.image?.let { image ->
+                    Image(
+                        provider = image.image,
+                        contentDescription = image.description,
+                        modifier = GlanceModifier.fillMaxWidth().defaultWeight(),
+                        contentScale = ContentScale.Crop
+                    )
+                    Spacer(modifier = GlanceModifier.height(16.dp))
                 }
-                Button(it)
+                Row(modifier = GlanceModifier.fillMaxWidth()) {
+                    TextSection(textList(it.title, it.subtitle))
+                    Spacer(modifier = GlanceModifier.defaultWeight())
+                    it.button?.let { button -> TemplateButton(button) }
+                }
             }
         }
     }
@@ -72,123 +86,113 @@
     @Composable
     override fun WidgetLayoutHorizontal() {
         getData(currentState()).let {
-            Column(
-                modifier = GlanceModifier.fillMaxSize().padding(8.dp).background(it.backgroundColor)
-            ) {
-                Row(modifier = GlanceModifier.fillMaxWidth()) {
+            var modifier = GlanceModifier.fillMaxSize().padding(16.dp)
+            it.backgroundColor?.let { color -> modifier = modifier.background(color) }
+            Row(modifier = modifier) {
+                Column(
+                    modifier =
+                    GlanceModifier.fillMaxHeight().background(Color.Transparent).defaultWeight()
+                ) {
                     TemplateHeader(it.headerIcon, it.header)
+                    Spacer(modifier = GlanceModifier.height(16.dp))
+                    Spacer(modifier = GlanceModifier.defaultWeight())
+
+                    // TODO: Extract small height as template constant
+                    val body =
+                        if (LocalSize.current.height > 240.dp) {
+                            it.body
+                        } else {
+                            null
+                        }
+                    TextSection(textList(it.title, it.subtitle, body))
+                    it.button?.let { button ->
+                        Spacer(modifier = GlanceModifier.height(16.dp))
+                        TemplateButton(button)
+                    }
                 }
-                Row(modifier = GlanceModifier.fillMaxWidth()) {
-                    Column(modifier = GlanceModifier.defaultWeight()) {
-                        TextBlock(it.title, it.subtitle, it.bodyText)
-                        Button(it)
-                    }
-                    it.mainImage?.let { image ->
-                        Image(
-                            provider = image.image,
-                            contentDescription = image.description,
-                            modifier = GlanceModifier.defaultWeight()
-                        )
-                    }
+                it.image?.let { image ->
+                    Spacer(modifier = GlanceModifier.width(16.dp))
+                    Image(
+                        provider = image.image,
+                        contentDescription = image.description,
+                        modifier = GlanceModifier.fillMaxHeight().defaultWeight(),
+                        contentScale = ContentScale.Crop
+                    )
                 }
             }
         }
     }
 
-    @Composable
-    private fun TextBlock(title: String?, subtitle: String?, body: String?) {
-        // TODO: Text color customization
-        val color = ColorProvider(R.color.text_default)
-        Column {
-            title?.let {
-                Text(title, style = TextStyle(fontSize = 36.sp, color = color), maxLines = 2)
-            }
-            subtitle?.let {
-                Text(it, style = TextStyle(fontSize = 20.sp, color = color), maxLines = 2)
-            }
-            body?.let { Text(it, style = TextStyle(fontSize = 18.sp, color = color), maxLines = 5) }
+    private fun textList(
+        title: TemplateText? = null,
+        subtitle: TemplateText? = null,
+        body: TemplateText? = null
+    ): List<TypedTemplateText> {
+        val result = mutableListOf<TypedTemplateText>()
+        title?.let {
+            result.add(TypedTemplateText(it, TemplateTextType.Title))
         }
-    }
+        subtitle?.let {
+            result.add(TypedTemplateText(it, TemplateTextType.Label))
+        }
+        body?.let {
+            result.add(TypedTemplateText(it, TemplateTextType.Body))
+        }
 
-    @Composable
-    private fun Button(data: Data) {
-        // If a button image is provided, use an image button
-        when (data.button) {
-            is TemplateImageButton -> {
-                // TODO: specify sizing for image button
-                val image = data.button.image
-                Image(
-                    provider = image.image,
-                    contentDescription = image.description,
-                    modifier = GlanceModifier.clickable(data.button.action)
-                )
-            }
-            is TemplateTextButton -> {
-                Button(text = data.button.text, onClick = data.button.action)
-            }
-            else -> {
-                Log.e(TAG, "Unrecognized button type: ${data.button}")
-            }
-        }
+        return result
     }
 
     /**
      * The semantic data required to build [SingleEntityTemplate] layouts.
      *
-     * @param backgroundColor Glanceable background color
      * @param headerIcon Logo icon, displayed in the glanceable header
-     * @param header Main header text
-     * @param title Text section main title
-     * @param subtitle Text section subtitle
-     * @param bodyText Text section body text
+     * @param header Main header text, text priority is ignored in default layouts
+     * @param title Text section main title, priority ordered
+     * @param subtitle Text section subtitle, priority ordered
+     * @param body Text section body text, priority ordered
      * @param button Action button
-     * @param mainImage Main image content
+     * @param image Main image content
+     * @param backgroundColor Glanceable background color
      */
-    // TODO: add optional ordering for text vs image sections, determine granularity level for
-    //  setting text colors
     class Data(
-        val backgroundColor: ColorProvider,
         val headerIcon: TemplateImageWithDescription,
-        val header: String? = null,
-        val title: String? = null,
-        val subtitle: String? = null,
-        val bodyText: String? = null,
+        val header: TemplateText? = null,
+        val title: TemplateText? = null,
+        val subtitle: TemplateText? = null,
+        val body: TemplateText? = null,
         val button: TemplateButton? = null,
-        val mainImage: TemplateImageWithDescription? = null,
+        val image: TemplateImageWithDescription? = null,
+        val backgroundColor: ColorProvider? = null
     ) {
 
+        override fun hashCode(): Int {
+            var result = headerIcon.hashCode()
+            result = 31 * result + (header?.hashCode() ?: 0)
+            result = 31 * result + (title?.hashCode() ?: 0)
+            result = 31 * result + (subtitle?.hashCode() ?: 0)
+            result = 31 * result + (body?.hashCode() ?: 0)
+            result = 31 * result + (button?.hashCode() ?: 0)
+            result = 31 * result + (image?.hashCode() ?: 0)
+            result = 31 * result + (backgroundColor?.hashCode() ?: 0)
+            return result
+        }
+
         override fun equals(other: Any?): Boolean {
             if (this === other) return true
             if (javaClass != other?.javaClass) return false
 
             other as Data
 
-            if (backgroundColor != other.backgroundColor) return false
             if (headerIcon != other.headerIcon) return false
             if (header != other.header) return false
             if (title != other.title) return false
             if (subtitle != other.subtitle) return false
-            if (bodyText != other.bodyText) return false
+            if (body != other.body) return false
             if (button != other.button) return false
-            if (mainImage != other.mainImage) return false
+            if (image != other.image) return false
+            if (backgroundColor != other.backgroundColor) return false
 
             return true
         }
-
-        override fun hashCode(): Int {
-            var result = backgroundColor.hashCode()
-            result = 31 * result + headerIcon.hashCode()
-            result = 31 * result + (header?.hashCode() ?: 0)
-            result = 31 * result + (title?.hashCode() ?: 0)
-            result = 31 * result + (subtitle?.hashCode() ?: 0)
-            result = 31 * result + (bodyText?.hashCode() ?: 0)
-            result = 31 * result + (button?.hashCode() ?: 0)
-            result = 31 * result + (mainImage?.hashCode() ?: 0)
-            return result
-        }
-    }
-
-    private companion object {
-        private const val TAG = "SingleEntityTemplate"
     }
 }
diff --git a/template/template/src/main/java/androidx/template/template/TemplateTranslator.kt b/template/template/src/main/java/androidx/template/template/TemplateTranslator.kt
index d005c82..669aaea 100644
--- a/template/template/src/main/java/androidx/template/template/TemplateTranslator.kt
+++ b/template/template/src/main/java/androidx/template/template/TemplateTranslator.kt
@@ -31,7 +31,7 @@
         // TODO: pass in host context and get layout for display params, including surface type etc.
         val height = LocalSize.current.height
         val width = LocalSize.current.width
-        if (height < Dp(240f) && width < Dp(240f)) {
+        if (height <= Dp(240f) && width <= Dp(240f)) {
             template.WidgetLayoutCollapsed()
         } else if ((width / height) < (3.0 / 2.0)) {
             template.WidgetLayoutVertical()
diff --git a/textclassifier/textclassifier/src/androidTest/java/androidx/textclassifier/MatchMakerImplTest.java b/textclassifier/textclassifier/src/androidTest/java/androidx/textclassifier/MatchMakerImplTest.java
index e385cef..81dd12f 100644
--- a/textclassifier/textclassifier/src/androidTest/java/androidx/textclassifier/MatchMakerImplTest.java
+++ b/textclassifier/textclassifier/src/androidTest/java/androidx/textclassifier/MatchMakerImplTest.java
@@ -74,6 +74,7 @@
     private String mSms;
 
     @Before
+    @SuppressWarnings("deprecation")
     public void setUp() {
         mContext = ApplicationProvider.getApplicationContext();
         mPackageManager = mock(PackageManager.class);
@@ -134,6 +135,7 @@
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void noMatchingApp() throws Exception {
         final PackageManager packageManager = mock(PackageManager.class);
         when(packageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(null);
@@ -147,6 +149,7 @@
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void noMatchingActivity() throws Exception {
         final PackageManager packageManager = mock(PackageManager.class);
         when(packageManager.resolveActivity(any(Intent.class), anyInt()))
diff --git a/textclassifier/textclassifier/src/main/java/androidx/textclassifier/LegacyTextClassifier.java b/textclassifier/textclassifier/src/main/java/androidx/textclassifier/LegacyTextClassifier.java
index adb2397..180d92a 100644
--- a/textclassifier/textclassifier/src/main/java/androidx/textclassifier/LegacyTextClassifier.java
+++ b/textclassifier/textclassifier/src/main/java/androidx/textclassifier/LegacyTextClassifier.java
@@ -329,6 +329,7 @@
         }
 
         @Nullable
+        @SuppressWarnings("deprecation")
         private RemoteActionCompat createRemoteAction(
                 Intent intent, String title, String description, int requestCode) {
             final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0);
@@ -364,6 +365,7 @@
         }
 
         @Nullable
+        @SuppressWarnings("deprecation")
         private PendingIntent createPendingIntent(Intent intent, int requestCode) {
             final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0);
             final int flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
diff --git a/vectordrawable/integration-tests/testapp/src/main/java/com/example/android/support/vectordrawable/app/SupportVectorDrawableDemos.java b/vectordrawable/integration-tests/testapp/src/main/java/com/example/android/support/vectordrawable/app/SupportVectorDrawableDemos.java
index c07087f..6b895f9 100644
--- a/vectordrawable/integration-tests/testapp/src/main/java/com/example/android/support/vectordrawable/app/SupportVectorDrawableDemos.java
+++ b/vectordrawable/integration-tests/testapp/src/main/java/com/example/android/support/vectordrawable/app/SupportVectorDrawableDemos.java
@@ -55,6 +55,7 @@
         getListView().setTextFilterEnabled(true);
     }
 
+    @SuppressWarnings("deprecation")
     protected List<Map<String, Object>> getData(String prefix) {
         List<Map<String, Object>> myData = new ArrayList<Map<String, Object>>();
 
diff --git a/wear/compose/compose-material/api/current.txt b/wear/compose/compose-material/api/current.txt
index 752bbdee..d9022a7 100644
--- a/wear/compose/compose-material/api/current.txt
+++ b/wear/compose/compose-material/api/current.txt
@@ -300,12 +300,18 @@
   }
 
   public interface ScalingLazyListLayoutInfo {
+    method public androidx.compose.foundation.gestures.Orientation getOrientation();
+    method public boolean getReverseLayout();
     method public int getTotalItemsCount();
     method public int getViewportEndOffset();
+    method public long getViewportSize();
     method public int getViewportStartOffset();
     method public java.util.List<androidx.wear.compose.material.ScalingLazyListItemInfo> getVisibleItemsInfo();
+    property public abstract androidx.compose.foundation.gestures.Orientation orientation;
+    property public abstract boolean reverseLayout;
     property public abstract int totalItemsCount;
     property public abstract int viewportEndOffset;
+    property public abstract long viewportSize;
     property public abstract int viewportStartOffset;
     property public abstract java.util.List<androidx.wear.compose.material.ScalingLazyListItemInfo> visibleItemsInfo;
   }
diff --git a/wear/compose/compose-material/api/public_plus_experimental_current.txt b/wear/compose/compose-material/api/public_plus_experimental_current.txt
index 58e2e73..b67df1d 100644
--- a/wear/compose/compose-material/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-material/api/public_plus_experimental_current.txt
@@ -326,12 +326,18 @@
   }
 
   public interface ScalingLazyListLayoutInfo {
+    method public androidx.compose.foundation.gestures.Orientation getOrientation();
+    method public boolean getReverseLayout();
     method public int getTotalItemsCount();
     method public int getViewportEndOffset();
+    method public long getViewportSize();
     method public int getViewportStartOffset();
     method public java.util.List<androidx.wear.compose.material.ScalingLazyListItemInfo> getVisibleItemsInfo();
+    property public abstract androidx.compose.foundation.gestures.Orientation orientation;
+    property public abstract boolean reverseLayout;
     property public abstract int totalItemsCount;
     property public abstract int viewportEndOffset;
+    property public abstract long viewportSize;
     property public abstract int viewportStartOffset;
     property public abstract java.util.List<androidx.wear.compose.material.ScalingLazyListItemInfo> visibleItemsInfo;
   }
@@ -514,7 +520,6 @@
     method @androidx.compose.runtime.Composable public void CurvedTextSeparator(androidx.wear.compose.foundation.CurvedRowScope, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.foundation.CurvedTextStyle curvedTextStyle, optional androidx.wear.compose.foundation.ArcPaddingValues contentArcPadding);
     method @androidx.compose.runtime.Composable public void TextSeparator(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
-    method @androidx.compose.runtime.Composable public androidx.wear.compose.foundation.CurvedTextStyle timeCurvedTextStyle(optional long color, optional long background, optional long fontSize);
     method @androidx.compose.runtime.Composable public String timeFormat();
     method public androidx.wear.compose.material.TimeSource timeSource(String timeFormat);
     method @androidx.compose.runtime.Composable public androidx.compose.ui.text.TextStyle timeTextStyle(optional long color, optional long background, optional long fontSize);
diff --git a/wear/compose/compose-material/api/restricted_current.txt b/wear/compose/compose-material/api/restricted_current.txt
index 752bbdee..d9022a7 100644
--- a/wear/compose/compose-material/api/restricted_current.txt
+++ b/wear/compose/compose-material/api/restricted_current.txt
@@ -300,12 +300,18 @@
   }
 
   public interface ScalingLazyListLayoutInfo {
+    method public androidx.compose.foundation.gestures.Orientation getOrientation();
+    method public boolean getReverseLayout();
     method public int getTotalItemsCount();
     method public int getViewportEndOffset();
+    method public long getViewportSize();
     method public int getViewportStartOffset();
     method public java.util.List<androidx.wear.compose.material.ScalingLazyListItemInfo> getVisibleItemsInfo();
+    property public abstract androidx.compose.foundation.gestures.Orientation orientation;
+    property public abstract boolean reverseLayout;
     property public abstract int totalItemsCount;
     property public abstract int viewportEndOffset;
+    property public abstract long viewportSize;
     property public abstract int viewportStartOffset;
     property public abstract java.util.List<androidx.wear.compose.material.ScalingLazyListItemInfo> visibleItemsInfo;
   }
diff --git a/wear/compose/compose-material/benchmark/src/androidTest/java/androidx/wear/compose/material/benchmark/TimeTextBenchmark.kt b/wear/compose/compose-material/benchmark/src/androidTest/java/androidx/wear/compose/material/benchmark/TimeTextBenchmark.kt
index 8f9a295..91ddcbb 100644
--- a/wear/compose/compose-material/benchmark/src/androidTest/java/androidx/wear/compose/material/benchmark/TimeTextBenchmark.kt
+++ b/wear/compose/compose-material/benchmark/src/androidTest/java/androidx/wear/compose/material/benchmark/TimeTextBenchmark.kt
@@ -27,7 +27,7 @@
 import androidx.compose.testutils.benchmark.benchmarkLayoutPerf
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
-import androidx.wear.compose.foundation.BasicCurvedText
+import androidx.wear.compose.material.CurvedText
 import androidx.wear.compose.material.ExperimentalWearMaterialApi
 import androidx.wear.compose.material.MaterialTheme
 import androidx.wear.compose.material.Text
@@ -100,18 +100,16 @@
                 )
             },
             leadingCurvedContent = {
-                BasicCurvedText(
-                    text = "Leading content",
-                    style = TimeTextDefaults.timeCurvedTextStyle()
+                CurvedText(
+                    text = "Leading content"
                 )
             },
             textCurvedSeparator = {
                 CurvedTextSeparator()
             },
             trailingCurvedContent = {
-                BasicCurvedText(
-                    text = "Trailing content",
-                    style = TimeTextDefaults.timeCurvedTextStyle()
+                CurvedText(
+                    text = "Trailing content"
                 )
             },
         )
diff --git a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/TimeTextSample.kt b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/TimeTextSample.kt
index 9f7451f..f3d7874 100644
--- a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/TimeTextSample.kt
+++ b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/TimeTextSample.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
 import androidx.wear.compose.foundation.ArcPaddingValues
+import androidx.wear.compose.foundation.CurvedTextStyle
 import androidx.wear.compose.material.CurvedText
 import androidx.wear.compose.material.ExperimentalWearMaterialApi
 import androidx.wear.compose.material.Text
@@ -35,42 +36,45 @@
 @Sampled
 @Composable
 fun TimeTextWithCustomSeparator() {
+    val leadingTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Green)
+    val trailingTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
+    val separatorTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Magenta)
     TimeText(
         leadingLinearContent = {
             Text(
                 text = "Leading content",
-                style = TimeTextDefaults.timeTextStyle(color = Color.Green)
+                style = leadingTextStyle
             )
         },
         leadingCurvedContent = {
             CurvedText(
                 text = "Leading content",
-                style = TimeTextDefaults.timeCurvedTextStyle(color = Color.Green)
+                style = CurvedTextStyle(leadingTextStyle)
             )
         },
         trailingLinearContent = {
             Text(
                 text = "Trailing content",
-                style = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
+                style = trailingTextStyle
             )
         },
         trailingCurvedContent = {
             CurvedText(
                 text = "Trailing content",
-                style = TimeTextDefaults.timeCurvedTextStyle(color = Color.Yellow)
+                style = CurvedTextStyle(trailingTextStyle)
             )
         },
         textLinearSeparator = {
             Text(
                 modifier = Modifier.padding(horizontal = 8.dp),
                 text = "***",
-                style = TimeTextDefaults.timeTextStyle(color = Color.Magenta)
+                style = separatorTextStyle
             )
         },
         textCurvedSeparator = {
             CurvedText(
                 text = "***",
-                style = TimeTextDefaults.timeCurvedTextStyle(color = Color.Magenta),
+                style = CurvedTextStyle(separatorTextStyle),
                 contentArcPadding = ArcPaddingValues(angular = 8.dp)
             )
         }
diff --git a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyColumnTest.kt b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyColumnTest.kt
index 02d89f9..bda8c48 100644
--- a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyColumnTest.kt
+++ b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyColumnTest.kt
@@ -413,7 +413,6 @@
                         itemSizeDp * 3.5f + defaultItemSpacingDp * 2.5f
                     ),
                     scalingParams = ScalingLazyColumnDefaults.scalingParams(1.0f, 1.0f),
-                    contentPadding = PaddingValues(vertical = 100.dp)
                 ) {
                     items(5) {
                         Box(Modifier.requiredSize(itemSizeDp).testTag("Item:" + it))
diff --git a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfoTest.kt b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfoTest.kt
index 051684e..7db086f 100644
--- a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfoTest.kt
+++ b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfoTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.wear.compose.material
 
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.animateScrollBy
 import androidx.compose.foundation.gestures.scrollBy
 import androidx.compose.foundation.layout.Arrangement
@@ -34,6 +35,7 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
@@ -94,6 +96,85 @@
         }
     }
 
+    @Test
+    fun orientationIsCorrect() {
+        lateinit var state: ScalingLazyListState
+        rule.setContent {
+            ScalingLazyColumn(
+                state = rememberScalingLazyListState().also { state = it },
+                modifier = Modifier.requiredSize(
+                    itemSizeDp * 3.5f + defaultItemSpacingDp * 2.5f
+                ),
+                autoCentering = true,
+                contentPadding = PaddingValues(all = 0.dp)
+            ) {
+                items(5) {
+                    Box(Modifier.requiredSize(itemSizeDp))
+                }
+            }
+        }
+
+        // TODO(b/210654937): Remove the waitUntil once we no longer need 2 stage initialization
+        rule.waitUntil { state.initialized.value }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.orientation).isEqualTo(Orientation.Vertical)
+        }
+    }
+
+    @Test
+    fun reverseLayoutIsCorrectWhenNotReversed() {
+        lateinit var state: ScalingLazyListState
+        rule.setContent {
+            ScalingLazyColumn(
+                state = rememberScalingLazyListState().also { state = it },
+                modifier = Modifier.requiredSize(
+                    itemSizeDp * 3.5f + defaultItemSpacingDp * 2.5f
+                ),
+                autoCentering = true,
+                contentPadding = PaddingValues(all = 0.dp)
+            ) {
+                items(5) {
+                    Box(Modifier.requiredSize(itemSizeDp))
+                }
+            }
+        }
+
+        // TODO(b/210654937): Remove the waitUntil once we no longer need 2 stage initialization
+        rule.waitUntil { state.initialized.value }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.reverseLayout).isEqualTo(false)
+        }
+    }
+
+    @Test
+    fun reverseLayoutIsCorrectWhenReversed() {
+        lateinit var state: ScalingLazyListState
+        rule.setContent {
+            ScalingLazyColumn(
+                state = rememberScalingLazyListState().also { state = it },
+                modifier = Modifier.requiredSize(
+                    itemSizeDp * 3.5f + defaultItemSpacingDp * 2.5f
+                ),
+                autoCentering = true,
+                contentPadding = PaddingValues(all = 0.dp),
+                reverseLayout = true
+            ) {
+                items(5) {
+                    Box(Modifier.requiredSize(itemSizeDp))
+                }
+            }
+        }
+
+        // TODO(b/210654937): Remove the waitUntil once we no longer need 2 stage initialization
+        rule.waitUntil { state.initialized.value }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.reverseLayout).isEqualTo(true)
+        }
+    }
+
     @FlakyTest(bugId = 217762274)
     @Test
     fun visibleItemsAreCorrectSetExplicitInitialItemIndex() {
@@ -492,7 +573,6 @@
                     itemSizeDp * 3.5f + defaultItemSpacingDp * 2.5f
                 ),
                 scalingParams = ScalingLazyColumnDefaults.scalingParams(1.0f, 1.0f),
-                contentPadding = PaddingValues(vertical = 100.dp),
             ) {
                 items(5) {
                     Box(
@@ -585,7 +665,6 @@
                     itemSizeDp * 3.5f + defaultItemSpacingDp * 2.5f
                 ),
                 scalingParams = ScalingLazyColumnDefaults.scalingParams(1.0f, 1.0f),
-                contentPadding = PaddingValues(vertical = 100.dp)
             ) {
                 items(5) {
                     Box(Modifier.requiredSize(itemSizeDp))
@@ -876,7 +955,7 @@
     }
 
     @Test
-    fun viewportOffsetsAreCorrect() {
+    fun viewportOffsetsAndSizeAreCorrect() {
         val sizePx = 45
         val sizeDp = with(rule.density) { sizePx.toDp() }
         lateinit var state: ScalingLazyListState
@@ -896,11 +975,12 @@
         rule.runOnIdle {
             assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(0)
             assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx)
+            assertThat(state.layoutInfo.viewportSize).isEqualTo(IntSize(sizePx, sizePx))
         }
     }
 
     @Test
-    fun viewportOffsetsAreCorrectWithContentPadding() {
+    fun viewportOffsetsAndSizeAreCorrectWithContentPadding() {
         val sizePx = 45
         val startPaddingPx = 10
         val endPaddingPx = 15
@@ -925,6 +1005,7 @@
         rule.runOnIdle {
             assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(-startPaddingPx)
             assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx - startPaddingPx)
+            assertThat(state.layoutInfo.viewportSize).isEqualTo(IntSize(sizePx, sizePx))
         }
     }
 
diff --git a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/TimeTextTest.kt b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/TimeTextTest.kt
index 535e985..9fce750 100644
--- a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/TimeTextTest.kt
+++ b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/TimeTextTest.kt
@@ -29,13 +29,14 @@
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontStyle
 import androidx.compose.ui.unit.sp
-import androidx.wear.compose.foundation.BasicCurvedText
 import androidx.wear.compose.material.TimeTextDefaults.CurvedTextSeparator
+import java.util.Calendar
 import org.junit.Assert.assertEquals
 import org.junit.Rule
 import org.junit.Test
-import java.util.Calendar
 
 @ExperimentalWearMaterialApi
 class TimeTextTest {
@@ -105,10 +106,9 @@
                         )
                     },
                     leadingCurvedContent = {
-                        BasicCurvedText(
+                        CurvedText(
                             modifier = Modifier.testTag(CURVED_ITEM_TAG),
-                            text = "Leading content",
-                            style = TimeTextDefaults.timeCurvedTextStyle()
+                            text = "Leading content"
                         )
                     }
                 )
@@ -131,10 +131,9 @@
                         )
                     },
                     leadingCurvedContent = {
-                        BasicCurvedText(
+                        CurvedText(
                             modifier = Modifier.testTag(CURVED_ITEM_TAG),
-                            text = "Leading content",
-                            style = TimeTextDefaults.timeCurvedTextStyle()
+                            text = "Leading content"
                         )
                     }
                 )
@@ -156,10 +155,9 @@
                         )
                     },
                     trailingCurvedContent = {
-                        BasicCurvedText(
+                        CurvedText(
                             modifier = Modifier.testTag(CURVED_ITEM_TAG),
-                            text = "Trailing content",
-                            style = TimeTextDefaults.timeCurvedTextStyle()
+                            text = "Trailing content"
                         )
                     }
                 )
@@ -181,10 +179,9 @@
                         )
                     },
                     trailingCurvedContent = {
-                        BasicCurvedText(
+                        CurvedText(
                             modifier = Modifier.testTag(CURVED_ITEM_TAG),
-                            text = "Leading content",
-                            style = TimeTextDefaults.timeCurvedTextStyle()
+                            text = "Leading content"
                         )
                     }
                 )
@@ -330,9 +327,8 @@
             ConfiguredShapeScreen(true) {
                 TimeText(
                     trailingCurvedContent = {
-                        BasicCurvedText(
-                            text = "Trailing content",
-                            style = TimeTextDefaults.timeCurvedTextStyle()
+                        CurvedText(
+                            text = "Trailing content"
                         )
                     },
                     textLinearSeparator = {
@@ -391,15 +387,13 @@
             ConfiguredShapeScreen(true) {
                 TimeText(
                     leadingCurvedContent = {
-                        BasicCurvedText(
-                            text = "Leading content",
-                            style = TimeTextDefaults.timeCurvedTextStyle()
+                        CurvedText(
+                            text = "Leading content"
                         )
                     },
                     trailingCurvedContent = {
-                        BasicCurvedText(
-                            text = "Trailing content",
-                            style = TimeTextDefaults.timeCurvedTextStyle()
+                        CurvedText(
+                            text = "Trailing content"
                         )
                     },
                     textLinearSeparator = {
@@ -423,7 +417,7 @@
     // for CurvedText
     @Test
     fun changes_timeTextStyle_on_square_device() {
-        val timeState = mutableStateOf("testState")
+        val timeText = "testTime"
 
         val testTextStyle = TextStyle(
             color = Color.Green,
@@ -436,17 +430,52 @@
                     timeSource = object : TimeSource {
                         override val currentTime: String
                             @Composable
-                            get() = timeState.value
+                            get() = timeText
                     },
                     timeTextStyle = testTextStyle
                 )
             }
         }
-        val actualStyle = rule.textStyleOf(timeState.value)
+        val actualStyle = rule.textStyleOf(timeText)
         assertEquals(testTextStyle.color, actualStyle.color)
         assertEquals(testTextStyle.background, actualStyle.background)
         assertEquals(testTextStyle.fontSize, actualStyle.fontSize)
     }
+
+    @Test
+    fun changes_material_theme_on_square_device() {
+        val timeText = "testTime"
+
+        val testTextStyle = TextStyle(
+            color = Color.Green,
+            background = Color.Black,
+            fontStyle = FontStyle.Italic,
+            fontSize = 25.sp,
+            fontFamily = FontFamily.SansSerif
+        )
+        rule.setContent {
+            MaterialTheme(
+                typography = MaterialTheme.typography.copy(
+                    caption1 = testTextStyle)
+            ) {
+                ConfiguredShapeScreen(false) {
+                    TimeText(
+                        timeSource = object : TimeSource {
+                            override val currentTime: String
+                                @Composable
+                                get() = timeText
+                        }
+                    )
+                }
+            }
+        }
+        val actualStyle = rule.textStyleOf(timeText)
+        assertEquals(testTextStyle.color, actualStyle.color)
+        assertEquals(testTextStyle.background, actualStyle.background)
+        assertEquals(testTextStyle.fontSize, actualStyle.fontSize)
+        assertEquals(testTextStyle.fontStyle, actualStyle.fontStyle)
+        assertEquals(testTextStyle.fontFamily, actualStyle.fontFamily)
+    }
 }
 
 @ExperimentalWearMaterialApi
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumn.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumn.kt
index f224436..3ef1130 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumn.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumn.kt
@@ -40,6 +40,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clipToBounds
@@ -54,6 +55,7 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.offset
+import kotlinx.coroutines.flow.first
 
 /**
  * Receiver scope which is used by [ScalingLazyColumn].
@@ -363,6 +365,12 @@
             }
             if (initialized) {
                 LaunchedEffect(state) {
+                    snapshotFlow {
+                        (state.layoutInfo as DefaultScalingLazyListLayoutInfo)
+                            .readyForInitialization
+                    }.first {
+                        it
+                    }
                     state.scrollToInitialItem()
                 }
             }
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumnMeasure.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumnMeasure.kt
index edbb214..7f748f3 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumnMeasure.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumnMeasure.kt
@@ -17,10 +17,12 @@
 package androidx.wear.compose.material
 
 import androidx.compose.animation.core.Easing
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.lazy.LazyListItemInfo
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.lerp
 import kotlin.math.min
 import kotlin.math.roundToInt
@@ -395,7 +397,16 @@
     override val viewportEndOffset: Int,
     override val totalItemsCount: Int,
     val centerItemIndex: Int,
-    val centerItemScrollOffset: Int
+    val centerItemScrollOffset: Int,
+    override val reverseLayout: Boolean,
+    override val orientation: Orientation,
+    override val viewportSize: IntSize,
+    // Flag to indicate that we are ready for the second stage of initialization. Note that this
+    // flag will be false once initialization is complete and initialized == true.
+    internal val readyForInitialization: Boolean,
+    // Flag to indicate that initialization is complete and initial scroll index and offset have
+    // been set.
+    internal val initialized: Boolean
 ) : ScalingLazyListLayoutInfo
 
 internal class DefaultScalingLazyListItemInfo(
@@ -405,7 +416,7 @@
     override val offset: Int,
     override val size: Int,
     override val scale: Float,
-    override val alpha: Float,
+    override val alpha: Float
 ) : ScalingLazyListItemInfo {
     override fun toString(): String {
         return "DefaultScalingLazyListItemInfo(index=$index, key=$key, " +
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfo.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfo.kt
index 83ef1a8..02d3c62 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfo.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListLayoutInfo.kt
@@ -15,6 +15,9 @@
  */
 package androidx.wear.compose.material
 
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.IntSize
+
 /**
  * Contains useful information about the currently displayed layout state of [ScalingLazyColumn].
  * For example you can get the list of currently displayed item.
@@ -48,4 +51,19 @@
      * The total count of items passed to [ScalingLazyColumn].
      */
     val totalItemsCount: Int
+
+    /**
+     * The size of the viewport. It is the lazy list layout size including all the content paddings.
+     */
+    val viewportSize: IntSize
+
+    /**
+     * The orientation of the lazy list.
+     */
+    val orientation: Orientation
+
+    /**
+     * True if the direction of scrolling and layout is reversed.
+     */
+    val reverseLayout: Boolean
 }
\ No newline at end of file
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListState.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListState.kt
index 951201d..56768ac 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListState.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyListState.kt
@@ -17,6 +17,7 @@
 package androidx.wear.compose.material
 
 import androidx.compose.foundation.MutatePriority
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.ScrollScope
 import androidx.compose.foundation.gestures.ScrollableState
 import androidx.compose.foundation.lazy.LazyListItemInfo
@@ -30,6 +31,7 @@
 import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.listSaver
 import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.unit.IntSize
 import kotlin.math.roundToInt
 
 /**
@@ -102,17 +104,14 @@
             gapBetweenItemsPx.value == null || viewportHeightPx.value == null ||
             anchorType.value == null || reverseLayout.value == null ||
             beforeContentPaddingPx.value == null || autoCentering.value == null ||
-            layoutInfo.visibleItemsInfo.isEmpty()
+            !autoCentering.value!! || layoutInfo.visibleItemsInfo.isEmpty()
         ) {
             0
         } else {
-            if (autoCentering.value!! && layoutInfo.visibleItemsInfo.first().index == 0) {
-                if (anchorType.value == ScalingLazyListAnchorType.ItemStart) {
-                    viewportHeightPx.value!! / 2f
-                } else {
-                    viewportHeightPx.value!! / 2f -
-                        layoutInfo.visibleItemsInfo.first().unadjustedSize() / 2f
-                }.roundToInt() - gapBetweenItemsPx.value!!
+            if (layoutInfo.visibleItemsInfo.first().index == 0) {
+                calculateTopAutoCenteringPaddingPx(
+                    layoutInfo.visibleItemsInfo.first().unadjustedSize()
+                )
             } else {
                 0
             }
@@ -156,6 +155,8 @@
         } else {
             val visibleItemsInfo = mutableListOf<ScalingLazyListItemInfo>()
             val viewportHeightPx = viewportHeightPx.value!!
+            var newCenterItemIndex = 0
+            var newCenterItemScrollOffset = 0
 
             // The verticalAdjustment is used to allow for the extraPadding that the
             // ScalingLazyColumn employs to ensure that there are sufficient list items composed
@@ -189,8 +190,8 @@
                     centerItemInfo
                 )
 
-                val newCenterItemIndex = centerItemInfo.index
-                val newCenterItemScrollOffset = -centerItemInfo.offset
+                newCenterItemIndex = centerItemInfo.index
+                newCenterItemScrollOffset = -centerItemInfo.offset
 
                 // Find the adjusted position of the central item in the coordinate system of the
                 // underlying LazyColumn by adjusting for any scaling
@@ -270,27 +271,57 @@
                         return@forEach
                     }
                 }
-
-                val totalItemsCount =
-                    if (autoCentering.value!!) {
-                        (lazyListState.layoutInfo.totalItemsCount - 2).coerceAtLeast(0)
-                    } else {
-                        lazyListState.layoutInfo.totalItemsCount
-                    }
-
-                DefaultScalingLazyListLayoutInfo(
-                    visibleItemsInfo = visibleItemsInfo,
-                    totalItemsCount = totalItemsCount,
-                    viewportStartOffset = lazyListState.layoutInfo.viewportStartOffset +
-                        extraPaddingPx.value!!,
-                    viewportEndOffset = lazyListState.layoutInfo.viewportEndOffset -
-                        extraPaddingPx.value!!,
-                    centerItemIndex = if (initialized.value) newCenterItemIndex else 0,
-                    centerItemScrollOffset = if (initialized.value) newCenterItemScrollOffset else 0
-                )
-            } else {
-                EmptyScalingLazyListLayoutInfo
             }
+            val totalItemsCount =
+                if (autoCentering.value!!) {
+                    (lazyListState.layoutInfo.totalItemsCount - 2).coerceAtLeast(0)
+                } else {
+                    lazyListState.layoutInfo.totalItemsCount
+                }
+
+            // Decide if we are ready for the 2nd stage of initialization
+            // 1. autoCentering is off or
+            // 2. The list has no items or
+            // 3. the before content autoCentering Spacer has been sized.
+            // NOTE: 3a. It is possible, if the first real item in the list is large, that the size
+            // of the Spacer is 0.
+            val readyForInitialization =
+                // Not already initialized
+                !initialized.value && (
+                    // Not autoCentering
+                    !autoCentering.value!! || (
+                        lazyListState.layoutInfo.visibleItemsInfo.size >= 2 && (
+                            // or Empty list (other than the 2 spacers)
+                            lazyListState.layoutInfo.visibleItemsInfo.size == 2 ||
+                                // or first item is non-zero size
+                                lazyListState.layoutInfo.visibleItemsInfo.first().size > 0 ||
+                                // or first item is supposed to be zero size
+                                calculateTopAutoCenteringPaddingPx(
+                                    lazyListState.layoutInfo.visibleItemsInfo[1].size
+                                ) == 0
+                            )
+                        )
+                    )
+
+            DefaultScalingLazyListLayoutInfo(
+                visibleItemsInfo = visibleItemsInfo,
+                totalItemsCount = totalItemsCount,
+                viewportStartOffset = lazyListState.layoutInfo.viewportStartOffset +
+                    extraPaddingPx.value!!,
+                viewportEndOffset = lazyListState.layoutInfo.viewportEndOffset -
+                    extraPaddingPx.value!!,
+                centerItemIndex = if (initialized.value) newCenterItemIndex else 0,
+                centerItemScrollOffset = if (initialized.value) newCenterItemScrollOffset else 0,
+                reverseLayout = reverseLayout.value!!,
+                orientation = lazyListState.layoutInfo.orientation,
+                viewportSize = IntSize(
+                    width = lazyListState.layoutInfo.viewportSize.width,
+                    height = lazyListState.layoutInfo.viewportSize.height -
+                        extraPaddingPx.value!! * 2
+                ),
+                readyForInitialization = readyForInitialization,
+                initialized = initialized.value
+            )
         }
     }
 
@@ -455,6 +486,15 @@
     private fun discardAutoCenteringListItem(item: LazyListItemInfo): Boolean =
         autoCentering.value!! &&
             (item.index == 0 || item.index == lazyListState.layoutInfo.totalItemsCount - 1)
+
+    private fun calculateTopAutoCenteringPaddingPx(unadjustedFirstItemSize: Int): Int {
+        return (if (anchorType.value == ScalingLazyListAnchorType.ItemStart) {
+            viewportHeightPx.value!! / 2f
+        } else {
+            viewportHeightPx.value!! / 2f -
+                unadjustedFirstItemSize / 2f
+        }.roundToInt() - gapBetweenItemsPx.value!!).coerceAtLeast(0)
+    }
 }
 
 private fun LazyListLayoutInfo.findItemInfoWithIndex(index: Int): LazyListItemInfo? {
@@ -474,4 +514,7 @@
     override val viewportStartOffset = 0
     override val viewportEndOffset = 0
     override val totalItemsCount = 0
+    override val viewportSize = IntSize.Zero
+    override val orientation = Orientation.Vertical
+    override val reverseLayout = false
 }
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/TimeText.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/TimeText.kt
index 04d8099..f189c2a 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/TimeText.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/TimeText.kt
@@ -73,7 +73,7 @@
  */
 @ExperimentalWearMaterialApi
 @Composable
-fun TimeText(
+public fun TimeText(
     modifier: Modifier = Modifier,
     timeSource: TimeSource = TimeTextDefaults.timeSource(timeFormat()),
     timeTextStyle: TextStyle = TimeTextDefaults.timeTextStyle(),
@@ -162,7 +162,7 @@
 
     /**
      * Creates a [TextStyle] with default parameters used for showing time
-     * on square screens
+     * on square screens. By default a copy of MaterialTheme.typography.caption1 style is created
      *
      * @param color The main color
      * @param background The background color
@@ -170,33 +170,11 @@
      */
     @Composable
     public fun timeTextStyle(
-        color: Color = Color.White,
-        background: Color = Color.Transparent,
-        fontSize: TextUnit = MaterialTheme.typography.button.fontSize,
-    ) = TextStyle(
-        color = color,
-        background = background,
-        fontSize = fontSize
-    )
-
-    /**
-     * Creates a [CurvedTextStyle] with default parameters used for showing time
-     * on round screens
-     *
-     * @param color The main color
-     * @param background The background color
-     * @param fontSize The font size
-     */
-    @Composable
-    public fun timeCurvedTextStyle(
-        color: Color = Color.White,
-        background: Color = Color.Transparent,
-        fontSize: TextUnit = MaterialTheme.typography.button.fontSize,
-    ) = CurvedTextStyle(
-        color = color,
-        background = background,
-        fontSize = fontSize
-    )
+        color: Color = Color.Unspecified,
+        background: Color = Color.Unspecified,
+        fontSize: TextUnit = TextUnit.Unspecified,
+    ) = MaterialTheme.typography.caption1 +
+        TextStyle(color = color, background = background, fontSize = fontSize)
 
     /**
      * A default implementation of Separator shown between trailing/leading content and the time
@@ -228,7 +206,7 @@
     @Composable
     public fun CurvedRowScope.CurvedTextSeparator(
         modifier: Modifier = Modifier,
-        curvedTextStyle: CurvedTextStyle = timeCurvedTextStyle(),
+        curvedTextStyle: CurvedTextStyle = CurvedTextStyle(timeTextStyle()),
         contentArcPadding: ArcPaddingValues = ArcPaddingValues(angular = 4.dp)
     ) {
         CurvedText(
@@ -260,9 +238,6 @@
     fun timeSource(timeFormat: String): TimeSource = DefaultTimeSource(timeFormat)
 }
 
-@ExperimentalWearMaterialApi
-internal expect class DefaultTimeSource(timeFormat: String) : TimeSource
-
 /**
  *  An interface which is responsible for retrieving a formatted time.
  */
@@ -276,3 +251,6 @@
     val currentTime: String
         @Composable get
 }
+
+@ExperimentalWearMaterialApi
+internal expect class DefaultTimeSource(timeFormat: String) : TimeSource
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/TimeTextDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/TimeTextDemo.kt
index 73ced52..2d7364f 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/TimeTextDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/TimeTextDemo.kt
@@ -22,6 +22,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
 import androidx.wear.compose.foundation.BasicCurvedText
+import androidx.wear.compose.foundation.CurvedTextStyle
 import androidx.wear.compose.material.ExperimentalWearMaterialApi
 import androidx.wear.compose.material.Text
 import androidx.wear.compose.material.TimeText
@@ -37,17 +38,18 @@
 @OptIn(ExperimentalWearMaterialApi::class)
 @Composable
 fun TimeTextWithLeadingText() {
+    val textStyle = TimeTextDefaults.timeTextStyle(color = Color.Green)
     TimeText(
         leadingLinearContent = {
             Text(
                 text = "Leading content",
-                style = TimeTextDefaults.timeTextStyle(color = Color.Green)
+                style = textStyle
             )
         },
         leadingCurvedContent = {
             BasicCurvedText(
                 text = "Leading content",
-                style = TimeTextDefaults.timeCurvedTextStyle(color = Color.Green)
+                style = CurvedTextStyle(textStyle)
             )
         }
     )
@@ -56,17 +58,18 @@
 @OptIn(ExperimentalWearMaterialApi::class)
 @Composable
 fun TimeTextWithTrailingText() {
+    val textStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
     TimeText(
         trailingLinearContent = {
             Text(
                 text = "Trailing content",
-                style = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
+                style = textStyle
             )
         },
         trailingCurvedContent = {
             BasicCurvedText(
                 text = "Trailing content",
-                style = TimeTextDefaults.timeCurvedTextStyle(color = Color.Yellow)
+                style = CurvedTextStyle(textStyle)
             )
         }
     )
@@ -75,29 +78,31 @@
 @OptIn(ExperimentalWearMaterialApi::class)
 @Composable
 fun TimeTextWithLeadingAndTrailingText() {
+    val leadingTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Green)
+    val trailingTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
     TimeText(
         leadingLinearContent = {
             Text(
                 text = "Leading content",
-                style = TimeTextDefaults.timeTextStyle(color = Color.Green)
+                style = leadingTextStyle
             )
         },
         leadingCurvedContent = {
             BasicCurvedText(
                 text = "Leading content",
-                style = TimeTextDefaults.timeCurvedTextStyle(color = Color.Green)
+                style = CurvedTextStyle(leadingTextStyle)
             )
         },
         trailingLinearContent = {
             Text(
                 text = "Trailing content",
-                style = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
+                style = trailingTextStyle
             )
         },
         trailingCurvedContent = {
             BasicCurvedText(
                 text = "Trailing content",
-                style = TimeTextDefaults.timeCurvedTextStyle(color = Color.Yellow)
+                style = CurvedTextStyle(trailingTextStyle)
             )
         }
     )
@@ -106,29 +111,31 @@
 @OptIn(ExperimentalWearMaterialApi::class)
 @Composable
 fun TimeTextWithPadding() {
+    val leadingTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Green)
+    val trailingTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
     TimeText(
         leadingLinearContent = {
             Text(
                 text = "Leading content",
-                style = TimeTextDefaults.timeTextStyle(color = Color.Green)
+                style = leadingTextStyle
             )
         },
         leadingCurvedContent = {
             BasicCurvedText(
                 text = "Leading content",
-                style = TimeTextDefaults.timeCurvedTextStyle(color = Color.Green)
+                style = CurvedTextStyle(leadingTextStyle)
             )
         },
         trailingLinearContent = {
             Text(
                 text = "Trailing content",
-                style = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
+                style = trailingTextStyle
             )
         },
         trailingCurvedContent = {
             BasicCurvedText(
                 text = "Trailing content",
-                style = TimeTextDefaults.timeCurvedTextStyle(color = Color.Yellow)
+                style = CurvedTextStyle(trailingTextStyle)
             )
         },
         contentPadding = PaddingValues(8.dp)
diff --git a/wear/tiles/tiles-material/api/current.txt b/wear/tiles/tiles-material/api/current.txt
index 42f4d21..38c920e 100644
--- a/wear/tiles/tiles-material/api/current.txt
+++ b/wear/tiles/tiles-material/api/current.txt
@@ -20,8 +20,8 @@
     method public androidx.wear.tiles.material.Button.Builder setImageContent(String);
     method public androidx.wear.tiles.material.Button.Builder setSize(androidx.wear.tiles.DimensionBuilders.DpProp);
     method public androidx.wear.tiles.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, androidx.wear.tiles.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.tiles.material.Button.Builder setTextContent(String);
+    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, int);
   }
 
   public class ButtonColors {
@@ -153,6 +153,25 @@
     field public static final float GAP_START_ANGLE = -156.1f;
   }
 
+  public class Text implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.tiles.ColorBuilders.ColorProp getColor();
+    method public androidx.wear.tiles.LayoutElementBuilders.FontStyle getFontStyle();
+    method public float getLineHeight();
+    method public float getMaxLines();
+    method public androidx.wear.tiles.ModifiersBuilders.Modifiers getModifiers();
+    method public int getMultilineAlignment();
+    method public int getOverflow();
+    method public String getText();
+  }
+
+  public static final class Text.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Text.Builder();
+    method public androidx.wear.tiles.material.Text build();
+    method public androidx.wear.tiles.material.Text.Builder setColor(androidx.wear.tiles.ColorBuilders.ColorProp);
+    method public androidx.wear.tiles.material.Text.Builder setText(String);
+    method public androidx.wear.tiles.material.Text.Builder setTypography(int);
+  }
+
   public class TitleChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
     method public androidx.wear.tiles.ActionBuilders.Action getAction();
     method public androidx.wear.tiles.material.ChipColors getChipColors();
@@ -172,6 +191,21 @@
     method public androidx.wear.tiles.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
+  public class Typography {
+    field public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
+    field public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
+    field public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
+    field public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
+    field public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
+    field public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
+    field public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
+    field public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
+    field public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
+    field public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
+    field public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
+    field public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
+  }
+
 }
 
 package androidx.wear.tiles.material.layouts {
diff --git a/wear/tiles/tiles-material/api/public_plus_experimental_current.txt b/wear/tiles/tiles-material/api/public_plus_experimental_current.txt
index 42f4d21..38c920e 100644
--- a/wear/tiles/tiles-material/api/public_plus_experimental_current.txt
+++ b/wear/tiles/tiles-material/api/public_plus_experimental_current.txt
@@ -20,8 +20,8 @@
     method public androidx.wear.tiles.material.Button.Builder setImageContent(String);
     method public androidx.wear.tiles.material.Button.Builder setSize(androidx.wear.tiles.DimensionBuilders.DpProp);
     method public androidx.wear.tiles.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, androidx.wear.tiles.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.tiles.material.Button.Builder setTextContent(String);
+    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, int);
   }
 
   public class ButtonColors {
@@ -153,6 +153,25 @@
     field public static final float GAP_START_ANGLE = -156.1f;
   }
 
+  public class Text implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.tiles.ColorBuilders.ColorProp getColor();
+    method public androidx.wear.tiles.LayoutElementBuilders.FontStyle getFontStyle();
+    method public float getLineHeight();
+    method public float getMaxLines();
+    method public androidx.wear.tiles.ModifiersBuilders.Modifiers getModifiers();
+    method public int getMultilineAlignment();
+    method public int getOverflow();
+    method public String getText();
+  }
+
+  public static final class Text.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Text.Builder();
+    method public androidx.wear.tiles.material.Text build();
+    method public androidx.wear.tiles.material.Text.Builder setColor(androidx.wear.tiles.ColorBuilders.ColorProp);
+    method public androidx.wear.tiles.material.Text.Builder setText(String);
+    method public androidx.wear.tiles.material.Text.Builder setTypography(int);
+  }
+
   public class TitleChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
     method public androidx.wear.tiles.ActionBuilders.Action getAction();
     method public androidx.wear.tiles.material.ChipColors getChipColors();
@@ -172,6 +191,21 @@
     method public androidx.wear.tiles.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
+  public class Typography {
+    field public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
+    field public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
+    field public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
+    field public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
+    field public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
+    field public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
+    field public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
+    field public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
+    field public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
+    field public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
+    field public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
+    field public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
+  }
+
 }
 
 package androidx.wear.tiles.material.layouts {
diff --git a/wear/tiles/tiles-material/api/restricted_current.txt b/wear/tiles/tiles-material/api/restricted_current.txt
index 42f4d21..38c920e 100644
--- a/wear/tiles/tiles-material/api/restricted_current.txt
+++ b/wear/tiles/tiles-material/api/restricted_current.txt
@@ -20,8 +20,8 @@
     method public androidx.wear.tiles.material.Button.Builder setImageContent(String);
     method public androidx.wear.tiles.material.Button.Builder setSize(androidx.wear.tiles.DimensionBuilders.DpProp);
     method public androidx.wear.tiles.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, androidx.wear.tiles.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.tiles.material.Button.Builder setTextContent(String);
+    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, int);
   }
 
   public class ButtonColors {
@@ -153,6 +153,25 @@
     field public static final float GAP_START_ANGLE = -156.1f;
   }
 
+  public class Text implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method public androidx.wear.tiles.ColorBuilders.ColorProp getColor();
+    method public androidx.wear.tiles.LayoutElementBuilders.FontStyle getFontStyle();
+    method public float getLineHeight();
+    method public float getMaxLines();
+    method public androidx.wear.tiles.ModifiersBuilders.Modifiers getModifiers();
+    method public int getMultilineAlignment();
+    method public int getOverflow();
+    method public String getText();
+  }
+
+  public static final class Text.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Text.Builder();
+    method public androidx.wear.tiles.material.Text build();
+    method public androidx.wear.tiles.material.Text.Builder setColor(androidx.wear.tiles.ColorBuilders.ColorProp);
+    method public androidx.wear.tiles.material.Text.Builder setText(String);
+    method public androidx.wear.tiles.material.Text.Builder setTypography(int);
+  }
+
   public class TitleChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
     method public androidx.wear.tiles.ActionBuilders.Action getAction();
     method public androidx.wear.tiles.material.ChipColors getChipColors();
@@ -172,6 +191,21 @@
     method public androidx.wear.tiles.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
+  public class Typography {
+    field public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
+    field public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
+    field public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
+    field public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
+    field public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
+    field public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
+    field public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
+    field public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
+    field public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
+    field public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
+    field public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
+    field public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
+  }
+
 }
 
 package androidx.wear.tiles.material.layouts {
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java
index 440e987..60b0090 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java
@@ -34,21 +34,20 @@
 import androidx.annotation.RestrictTo.Scope;
 import androidx.wear.tiles.ActionBuilders.Action;
 import androidx.wear.tiles.ColorBuilders.ColorProp;
-import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
 import androidx.wear.tiles.DimensionBuilders.ContainerDimension;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
+import androidx.wear.tiles.LayoutElementBuilders;
 import androidx.wear.tiles.LayoutElementBuilders.Box;
 import androidx.wear.tiles.LayoutElementBuilders.ColorFilter;
 import androidx.wear.tiles.LayoutElementBuilders.FontStyle;
-import androidx.wear.tiles.LayoutElementBuilders.FontStyles;
 import androidx.wear.tiles.LayoutElementBuilders.Image;
 import androidx.wear.tiles.LayoutElementBuilders.LayoutElement;
-import androidx.wear.tiles.LayoutElementBuilders.Text;
 import androidx.wear.tiles.ModifiersBuilders;
 import androidx.wear.tiles.ModifiersBuilders.Background;
 import androidx.wear.tiles.ModifiersBuilders.Clickable;
 import androidx.wear.tiles.ModifiersBuilders.Corner;
 import androidx.wear.tiles.ModifiersBuilders.Modifiers;
+import androidx.wear.tiles.material.Typography.TypographyName;
 import androidx.wear.tiles.proto.LayoutElementProto;
 
 import java.lang.annotation.Retention;
@@ -92,14 +91,15 @@
         @NonNull private String mContentDescription = "";
         @NonNull private DpProp mSize = DEFAULT_BUTTON_SIZE;
         @Nullable private String mText = null;
-        @Nullable private FontStyle mFontStyle = null;
+        private @TypographyName int mTypographyName =
+                getDefaultTypographyForSize(DEFAULT_BUTTON_SIZE);
+        private boolean mIsTypographyNameSet = false;
         @Nullable private String mIcon = null;
         @Nullable private DpProp mIconSize = null;
         @Nullable private String mImage = null;
         @NonNull private ButtonColors mButtonColors = PRIMARY_BUTTON_COLORS;
         private @ButtonType int mType = NOT_SET;
         private boolean mDefaultSize = false;
-        @Nullable private DeviceParameters mDeviceParameters = null;
 
         /**
          * Creates a builder for the {@link Button} from the given content. Custom content should be
@@ -216,39 +216,38 @@
          * Sets the content of this Button to be the given text with the default font for the set
          * size (for the {@link ButtonDefaults#DEFAULT_BUTTON_SIZE}, {@link
          * ButtonDefaults#LARGE_BUTTON_SIZE} and {@link ButtonDefaults#EXTRA_LARGE_BUTTON_SIZE} is
-         * {@link FontStyles#title2}, {@link FontStyles#title1} and {@link FontStyles#display3}
-         * respectively). Any previously added content will be overridden. Text should contain no
-         * more than 3 characters, otherwise it will overflow from the edges.
+         * {@link Typography#TYPOGRAPHY_TITLE2}, {@link Typography#TYPOGRAPHY_TITLE1} and {@link
+         * Typography#TYPOGRAPHY_DISPLAY3} respectively). Any previously added content will be
+         * overridden. Text should contain no more than 3 characters, otherwise it will overflow
+         * from the edges.
          */
         // There are multiple methods to set different type of content, but there is general getter
         // getContent that will return LayoutElement set by any of them. b/217197259
         @NonNull
         @SuppressWarnings("MissingGetterMatchingBuilder")
-        public Builder setTextContent(
-                @NonNull String text, @NonNull DeviceParameters deviceParameters) {
+        public Builder setTextContent(@NonNull String text) {
             resetContent();
             this.mText = text;
-            this.mDeviceParameters = deviceParameters;
             this.mType = TEXT;
             this.mDefaultSize = true;
             return this;
         }
 
         /**
-         * Sets the content of this Button to be the given text with the given font. You shouldn't
-         * add color to your font as it will be overridden by the {@link
-         * ButtonColors#getContentColor}. Only {@link #setButtonColors} should be used for
-         * customizing the colors of the button. Any previously added content will be overridden.
-         * Text should contain no more than 3 characters, otherwise it will overflow from the edges.
+         * Sets the content of this Button to be the given text with the given font. If you need
+         * more font related customization, consider using {@link #setContent} with {@link Text}
+         * component. Any previously added content will be overridden. Text should contain no more
+         * than 3 characters, otherwise it will overflow from the edges.
          */
         // There are multiple methods to set different type of content, but there is general getter
         // getContent that will return LayoutElement set by any of them. b/217197259
         @NonNull
         @SuppressWarnings("MissingGetterMatchingBuilder")
-        public Builder setTextContent(@NonNull String text, @NonNull FontStyle font) {
+        public Builder setTextContent(@NonNull String text, @TypographyName int typographyName) {
             resetContent();
             this.mText = text;
-            this.mFontStyle = font;
+            this.mTypographyName = typographyName;
+            this.mIsTypographyNameSet = true;
             this.mType = TEXT;
             this.mDefaultSize = false;
             return this;
@@ -274,7 +273,7 @@
 
         private void resetContent() {
             this.mText = null;
-            this.mFontStyle = null;
+            this.mIsTypographyNameSet = false;
             this.mIcon = null;
             this.mImage = null;
             this.mCustomContent = null;
@@ -345,28 +344,16 @@
                 }
                 case TEXT:
                 {
-                    FontStyle fontStyle;
-                    if (mFontStyle != null) {
-                        fontStyle =
-                                FontStyle.fromProto(
-                                        mFontStyle.toProto().toBuilder()
-                                                .setColor(
-                                                        mButtonColors
-                                                                .getContentColor()
-                                                                .toProto())
-                                                .build());
-                    } else {
-                        fontStyle =
-                                getDefaultFontForSize(mSize, checkNotNull(mDeviceParameters))
-                                        .setColor(mButtonColors.getContentColor())
-                                        .build();
-                    }
-
+                    @TypographyName
+                    int typographyName =
+                            mIsTypographyNameSet
+                                    ? mTypographyName : getDefaultTypographyForSize(mSize);
                     mContent =
                             new Text.Builder()
-                                .setText(checkNotNull(mText))
-                                .setMaxLines(1)
-                                .setFontStyle(fontStyle);
+                                    .setText(checkNotNull(mText))
+                                    .setMaxLines(1)
+                                    .setTypography(typographyName)
+                                    .setColor(mButtonColors.getContentColor());
 
                     return mContent.build();
                 }
@@ -413,37 +400,33 @@
             }
         }
 
-        @NonNull
-        private FontStyle.Builder getDefaultFontForSize(
-                @NonNull DpProp size, @NonNull DeviceParameters deviceParameters) {
+        private @TypographyName int getDefaultTypographyForSize(@NonNull DpProp size) {
             if (size.getValue() == LARGE_BUTTON_SIZE.getValue()) {
-                return FontStyles.title1(deviceParameters);
+                return Typography.TYPOGRAPHY_TITLE1;
             } else {
                 if (size.getValue() == EXTRA_LARGE_BUTTON_SIZE.getValue()) {
-                    return FontStyles.display3(deviceParameters);
+                    return Typography.TYPOGRAPHY_DISPLAY3;
                 } else {
-                    return FontStyles.title2(deviceParameters);
+                    return Typography.TYPOGRAPHY_TITLE2;
                 }
             }
         }
     }
 
-    /** Returns the content of this Button. Intended for testing purposes only. */
+    /** Returns the content of this Button. */
     @NonNull
     public LayoutElement getContent() {
         return checkNotNull(mElement.getContents().get(0));
     }
 
-    /**
-     * Returns click event action associated with this Button. Intended for testing purposes only.
-     */
+    /** Returns click event action associated with this Button. */
     @NonNull
     public Action getAction() {
         return checkNotNull(
                 checkNotNull(checkNotNull(mElement.getModifiers()).getClickable()).getOnClick());
     }
 
-    /** Returns content description for this Button. Intended for testing purposes only. */
+    /** Returns content description for this Button. */
     @NonNull
     public String getContentDescription() {
         return checkNotNull(
@@ -451,7 +434,7 @@
                         .getContentDescription());
     }
 
-    /** Returns size for this Button. Intended for testing purposes only. */
+    /** Returns size for this Button. */
     @NonNull
     public ContainerDimension getSize() {
         return checkNotNull(mElement.getWidth());
@@ -462,7 +445,7 @@
                 checkNotNull(checkNotNull(mElement.getModifiers()).getBackground()).getColor());
     }
 
-    /** Returns button color of this Button. Intended for testing purposes only. */
+    /** Returns button color of this Button. */
     @NonNull
     public ButtonColors getButtonColors() {
         ColorProp backgroundColor = getBackgroundColor();
@@ -477,7 +460,11 @@
                 break;
             case Builder.TEXT:
                 contentColor =
-                        checkNotNull(checkNotNull(((Text) mainElement).getFontStyle()).getColor());
+                        checkNotNull(
+                                checkNotNull(
+                                                ((LayoutElementBuilders.Text) mainElement)
+                                                        .getFontStyle())
+                                        .getColor());
                 break;
             case Builder.IMAGE:
             case Builder.CUSTOM_CONTENT:
@@ -502,8 +489,9 @@
      * have color.
      */
     private @Builder.ButtonType int getType(LayoutElement element) {
-        if (element instanceof Text) {
-            FontStyle fontStyle = ((Text) element).getFontStyle();
+        // To elementary Text class as Material Text when it goes to proto disappears.
+        if (element instanceof LayoutElementBuilders.Text) {
+            FontStyle fontStyle = ((LayoutElementBuilders.Text) element).getFontStyle();
             if (fontStyle != null && fontStyle.getColor() != null) {
                 return Builder.TEXT;
             }
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java
index 0391a36..9c74f0d 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java
@@ -43,20 +43,18 @@
 import androidx.wear.tiles.LayoutElementBuilders.Box;
 import androidx.wear.tiles.LayoutElementBuilders.ColorFilter;
 import androidx.wear.tiles.LayoutElementBuilders.Column;
-import androidx.wear.tiles.LayoutElementBuilders.FontStyle;
-import androidx.wear.tiles.LayoutElementBuilders.FontStyles;
 import androidx.wear.tiles.LayoutElementBuilders.HorizontalAlignment;
 import androidx.wear.tiles.LayoutElementBuilders.Image;
 import androidx.wear.tiles.LayoutElementBuilders.LayoutElement;
 import androidx.wear.tiles.LayoutElementBuilders.Row;
 import androidx.wear.tiles.LayoutElementBuilders.Spacer;
-import androidx.wear.tiles.LayoutElementBuilders.Text;
 import androidx.wear.tiles.ModifiersBuilders;
 import androidx.wear.tiles.ModifiersBuilders.Background;
 import androidx.wear.tiles.ModifiersBuilders.Clickable;
 import androidx.wear.tiles.ModifiersBuilders.Corner;
 import androidx.wear.tiles.ModifiersBuilders.Modifiers;
 import androidx.wear.tiles.ModifiersBuilders.Padding;
+import androidx.wear.tiles.material.Typography.TypographyName;
 import androidx.wear.tiles.proto.LayoutElementProto;
 
 import java.lang.annotation.Retention;
@@ -68,10 +66,10 @@
  * optional icon or with custom content.
  *
  * <p>The Chip is Stadium shape and has a max height designed to take no more than two lines of text
- * of {@link FontStyles#button} style. The {@link Chip} can have an icon horizontally parallel to
- * the two lines of text. Width of chip can very, and the recommended size is screen dependent with
- * the recommended margin being defined in {@link ChipDefaults#DEFAULT_MARGIN_PERCENT} which is set
- * by default.
+ * of {@link Typography#TYPOGRAPHY_BUTTON} style. The {@link Chip} can have an icon horizontally
+ * parallel to the two lines of text. Width of chip can very, and the recommended size is screen
+ * dependent with the recommended margin being defined in
+ * {@link ChipDefaults#DEFAULT_MARGIN_PERCENT} which is set by default.
  *
  * <p>The recommended set of {@link ChipColors} styles can be obtained from {@link ChipDefaults}.,
  * e.g. {@link ChipDefaults#PRIMARY} to get a color scheme for a primary {@link Chip} which by
@@ -105,14 +103,13 @@
         @Nullable private String mLabelText = null;
         @NonNull private final Action mAction;
         @NonNull private final String mClickableId;
-        @NonNull private final DeviceParameters mDeviceParameters;
         @NonNull private String mContentDescription = "";
         @NonNull private ContainerDimension mWidth;
         @NonNull private DpProp mHeight = DEFAULT_HEIGHT;
         @NonNull private ChipColors mChipColors = PRIMARY;
         private @ChipType int mType = NOT_SET;
         private @HorizontalAlignment int mHorizontalAlign = HORIZONTAL_ALIGN_START;
-        @NonNull private FontStyle mPrimaryTextFont;
+        @TypographyName private int mPrimaryTextTypography;
         @NonNull private DpProp mHorizontalPadding = HORIZONTAL_PADDING;
         @NonNull private DpProp mVerticalPadding = VERTICAL_PADDING;
 
@@ -123,7 +120,7 @@
          * @param action Associated Actions for click events. When the Chip is clicked it will fire
          *     the associated action.
          * @param clickableId The ID associated with the given action's clickable.
-         * @param deviceParameters The device parameters used for styling text.
+         * @param deviceParameters The device parameters used to derive defaults for this Chip.
          */
         @SuppressWarnings("LambdaLast")
         public Builder(
@@ -132,13 +129,12 @@
                 @NonNull DeviceParameters deviceParameters) {
             mAction = action;
             mClickableId = clickableId;
-            mDeviceParameters = deviceParameters;
             mWidth =
                     dp(
                             (100 - 2 * DEFAULT_MARGIN_PERCENT)
                                     * deviceParameters.getScreenWidthDp()
                                     / 100);
-            mPrimaryTextFont = FontStyles.button(deviceParameters).build();
+            mPrimaryTextTypography = Typography.TYPOGRAPHY_BUTTON;
         }
 
         /**
@@ -203,8 +199,8 @@
          * <p>Sets the font for the primary text and should only be used internally.
          */
         @NonNull
-        Builder setPrimaryTextFontStyle(@NonNull FontStyle fontStyle) {
-            this.mPrimaryTextFont = fontStyle;
+        Builder setPrimaryTextTypography(@TypographyName int typography) {
+            this.mPrimaryTextTypography = typography;
             return this;
         }
 
@@ -276,7 +272,6 @@
             return this;
         }
 
-        // TODO(b/207350548): In RTL mode, should icon still be on the left.
         /**
          * Sets the horizontal alignment in the chip. It is strongly recommended that the content of
          * the chip is start-aligned if there is more than primary text in it. If not set, {@link
@@ -372,7 +367,7 @@
             if (mType == NOT_SET) {
                 throw new IllegalStateException(
                         "No content set. Use setPrimaryTextContent or similar method to add"
-                                + " content");
+                            + " content");
             }
             if (mType == CUSTOM_CONTENT) {
                 return checkNotNull(mCustomContent);
@@ -380,7 +375,8 @@
             Text mainTextElement =
                     new Text.Builder()
                             .setText(mPrimaryText)
-                            .setFontStyle(customizeFontStyle())
+                            .setTypography(mPrimaryTextTypography)
+                            .setColor(mChipColors.getContentColor())
                             .setMaxLines(getCorrectMaxLines())
                             .setOverflow(LayoutElementBuilders.TEXT_OVERFLOW_TRUNCATE)
                             .setMultilineAlignment(LayoutElementBuilders.TEXT_ALIGN_START)
@@ -395,10 +391,8 @@
                 Text labelTextElement =
                         new Text.Builder()
                                 .setText(mLabelText)
-                                .setFontStyle(
-                                        FontStyles.caption2(mDeviceParameters)
-                                                .setColor(mChipColors.getSecondaryContentColor())
-                                                .build())
+                                .setTypography(Typography.TYPOGRAPHY_CAPTION2)
+                                .setColor(mChipColors.getSecondaryContentColor())
                                 .setMaxLines(1)
                                 .setOverflow(LayoutElementBuilders.TEXT_OVERFLOW_TRUNCATE)
                                 .setMultilineAlignment(LayoutElementBuilders.TEXT_ALIGN_START)
@@ -431,13 +425,6 @@
             }
         }
 
-        private FontStyle customizeFontStyle() {
-            return FontStyle.fromProto(
-                    mPrimaryTextFont.toProto().toBuilder()
-                            .setColor(mChipColors.getContentColor().toProto())
-                            .build());
-        }
-
         private int getCorrectMaxLines() {
             if (mMaxLines > 0) {
                 return mMaxLines;
@@ -503,13 +490,17 @@
             if (contents.size() == 1 || contents.size() == 2) {
                 // This is potentially our chip and this part contains 1 or 2 lines of text.
                 LayoutElement element = contents.get(0);
-                if (element instanceof Text) {
-                    contentColor = getTextColorFromContent((Text) element);
+                // To elementary Text class as Material Text when it goes to proto disappears.
+                if (element instanceof LayoutElementBuilders.Text) {
+                    contentColor = getTextColorFromContent((LayoutElementBuilders.Text) element);
 
                     if (contents.size() == 2) {
                         element = contents.get(1);
-                        if (element instanceof Text) {
-                            secondaryContentColor = getTextColorFromContent((Text) element);
+                        // To elementary Text class as Material Text when it goes to proto
+                        // disappears.
+                        if (element instanceof LayoutElementBuilders.Text) {
+                            secondaryContentColor =
+                                    getTextColorFromContent((LayoutElementBuilders.Text) element);
                         }
                     }
                 }
@@ -530,7 +521,7 @@
         return new ChipColors(backgroundColor, iconTintColor, contentColor, secondaryContentColor);
     }
 
-    private ColorProp getTextColorFromContent(Text text) {
+    private ColorProp getTextColorFromContent(LayoutElementBuilders.Text text) {
         ColorProp color = new ColorProp.Builder().build();
         if (text.getFontStyle() != null && text.getFontStyle().getColor() != null) {
             color = checkNotNull(checkNotNull(text.getFontStyle()).getColor());
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CircularProgressIndicator.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CircularProgressIndicator.java
index 1f4eea9..8455984 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CircularProgressIndicator.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CircularProgressIndicator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -45,15 +45,14 @@
 
 /**
  * Tiles component {@link CircularProgressIndicator} that represents circular progress indicator
- * which supports a gap in the circular track between startAngle and endAngle.
- * [Progress Indicator doc]
- * (https://developer.android.com/training/wearables/components/progress-indicator)
+ * which supports a gap in the circular track between startAngle and endAngle. [Progress Indicator
+ * doc] (https://developer.android.com/training/wearables/components/progress-indicator)
  *
  * <p>The CircularProgressIndicator is a colored arc around the edge of the screen with the given
  * start and end angles, which can describe a full or partial circle. Behind it is an arc with
- * optional gap representing full progress. The recommended sizes are defined in
- * {@link ProgressIndicatorDefaults}. Unless specified, the CircularProgressIndicator will have
- * the full length.
+ * optional gap representing full progress. The recommended sizes are defined in {@link
+ * ProgressIndicatorDefaults}. Unless specified, the CircularProgressIndicator will have the full
+ * length.
  *
  * <p>The recommended set of {@link ProgressIndicatorColors} can be obtained from {@link
  * ProgressIndicatorDefaults}., e.g. {@link ProgressIndicatorDefaults#DEFAULT_COLOR} to get a color
@@ -192,8 +191,8 @@
                             .addContent(
                                     new ArcLine.Builder()
                                             .setColor(
-                                                    mCircularProgressIndicatorColors.getTrackColor()
-                                            )
+                                                    mCircularProgressIndicatorColors
+                                                            .getTrackColor())
                                             .setThickness(mStrokeWidth)
                                             .setLength(length)
                                             .build())
@@ -207,8 +206,7 @@
                                     new ArcLine.Builder()
                                             .setColor(
                                                     mCircularProgressIndicatorColors
-                                                            .getIndicatorColor()
-                                            )
+                                                            .getIndicatorColor())
                                             .setThickness(mStrokeWidth)
                                             .setLength(degrees(mProgress * length.getValue()))
                                             .build());
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CompactChip.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CompactChip.java
index b7021fb..55b9eaa 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CompactChip.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CompactChip.java
@@ -28,7 +28,6 @@
 import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
 import androidx.wear.tiles.DimensionBuilders.ContainerDimension;
 import androidx.wear.tiles.DimensionBuilders.WrappedDimensionProp;
-import androidx.wear.tiles.LayoutElementBuilders.FontStyles;
 import androidx.wear.tiles.LayoutElementBuilders.LayoutElement;
 import androidx.wear.tiles.proto.LayoutElementProto;
 
@@ -36,7 +35,8 @@
  * Tiles component {@link CompactChip} that represents clickable object with the text.
  *
  * <p>The Chip is Stadium shape and has a max height designed to take no more than one line of text
- * of {@link FontStyles#caption1} style. Width of the chip is adjustable to the text size.
+ * of {@link Typography#TYPOGRAPHY_CAPTION1} style. Width of the chip is adjustable to the text
+ * size.
  *
  * <p>The recommended set of {@link ChipColors} styles can be obtained from {@link ChipDefaults}.,
  * e.g. {@link ChipDefaults#COMPACT_PRIMARY} to get a color scheme for a primary {@link CompactChip}
@@ -105,8 +105,7 @@
                             .setHeight(COMPACT_HEIGHT)
                             .setHorizontalPadding(COMPACT_HORIZONTAL_PADDING)
                             .setPrimaryTextContent(mText)
-                            .setPrimaryTextFontStyle(
-                                    FontStyles.caption1(mDeviceParameters).build());
+                            .setPrimaryTextTypography(Typography.TYPOGRAPHY_CAPTION1);
 
             return new CompactChip(chipBuilder.build());
         }
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Helper.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Helper.java
index 85adda7..08c5d74 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Helper.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Helper.java
@@ -37,7 +37,10 @@
 
     /**
      * Returns given value if not null or throws {@code NullPointerException} otherwise.
+     *
+     * @hide
      */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
     public static <T> T checkNotNull(@Nullable T value) {
         if (value == null) {
@@ -53,7 +56,10 @@
 
     /**
      * Returns true if the given DeviceParameters belong to the round screen device.
+     *
+     * @hide
      */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     public static boolean isRoundDevice(@NonNull DeviceParameters deviceParameters) {
         return deviceParameters.getScreenShape() == DeviceParametersBuilders.SCREEN_SHAPE_ROUND;
     }
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Text.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Text.java
new file mode 100644
index 0000000..1d94bb5
--- /dev/null
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Text.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles.material;
+
+import static androidx.wear.tiles.ColorBuilders.argb;
+import static androidx.wear.tiles.LayoutElementBuilders.TEXT_ALIGN_CENTER;
+import static androidx.wear.tiles.LayoutElementBuilders.TEXT_OVERFLOW_TRUNCATE;
+import static androidx.wear.tiles.material.Helper.checkNotNull;
+import static androidx.wear.tiles.material.Typography.TYPOGRAPHY_DISPLAY1;
+import static androidx.wear.tiles.material.Typography.getFontStyleBuilder;
+import static androidx.wear.tiles.material.Typography.getLineHeightForTypography;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.tiles.ColorBuilders.ColorProp;
+import androidx.wear.tiles.LayoutElementBuilders;
+import androidx.wear.tiles.LayoutElementBuilders.FontStyle;
+import androidx.wear.tiles.LayoutElementBuilders.LayoutElement;
+import androidx.wear.tiles.LayoutElementBuilders.TextAlignment;
+import androidx.wear.tiles.LayoutElementBuilders.TextOverflow;
+import androidx.wear.tiles.ModifiersBuilders.Modifiers;
+import androidx.wear.tiles.material.Typography.TypographyName;
+import androidx.wear.tiles.proto.LayoutElementProto;
+
+/**
+ * Tiles component {@link Text} that represents text object holding any information.
+ *
+ * <p>There are pre-built typography styles that can be obtained from constants in
+ * {@link Typography}.
+ */
+public class Text implements LayoutElement {
+    @NonNull private final LayoutElementBuilders.Text mText;
+
+    Text(@NonNull LayoutElementBuilders.Text mText) {
+        this.mText = mText;
+    }
+
+    /** Builder class for {@link Text}. */
+    public static final class Builder implements LayoutElement.Builder {
+        @NonNull private String mTextContent = "";
+        @NonNull private ColorProp mColor = argb(Colors.ON_PRIMARY);
+        private @TypographyName int mTypographyName = TYPOGRAPHY_DISPLAY1;
+        private boolean mItalic = false;
+        private int mMaxLines = 1;
+        private boolean mUnderline = false;
+        private @TextAlignment int mMultilineAlignment = TEXT_ALIGN_CENTER;
+        @NonNull private Modifiers mModifiers = new Modifiers.Builder().build();
+        private @TextOverflow int mOverflow = TEXT_OVERFLOW_TRUNCATE;
+
+        /** Sets the text content for the {@link Text}. */
+        @NonNull
+        public Builder setText(@NonNull String text) {
+            this.mTextContent = text;
+            return this;
+        }
+
+        /**
+         * Sets the typography for the {@link Text}. If not set,
+         * {@link Typography#TYPOGRAPHY_TITLE1} will be used.
+         */
+        @NonNull
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // There is getFontStyle matching getter for this setter as the serialized format of the
+        // ProtoLayouts do not allow for a direct reconstruction of the all arguments, but it has
+        // FontStyle object of that text.
+        public Builder setTypography(@TypographyName int typography) {
+            this.mTypographyName = typography;
+            return this;
+        }
+
+        /**
+         * Sets the color for the {@link Text}. If not set, {@link Colors#ON_PRIMARY} will be
+         * used.
+         */
+        @NonNull
+        public Builder setColor(@NonNull ColorProp color) {
+            this.mColor = color;
+            return this;
+        }
+
+        /** Sets the text to be italic. */
+        @NonNull
+        Builder setItalic(boolean italic) {
+            this.mItalic = italic;
+            return this;
+        }
+
+        /** Sets the text to be underlined. */
+        @NonNull
+        Builder setUnderline(boolean underline) {
+            this.mUnderline = underline;
+            return this;
+        }
+
+        /** Sets the maximum lines of text. If not set, 1 will be used. */
+        @NonNull
+        Builder setMaxLines(@IntRange(from = 1) int maxLines) {
+            this.mMaxLines = maxLines;
+            return this;
+        }
+
+        /**
+         * Sets the mutliline alignenment for text. If not set, {@link
+         * TextAlignment#TEXT_ALIGN_CENTER} will be used.
+         */
+        @NonNull
+        Builder setMultilineAlignment(@TextAlignment int multilineAlignment) {
+            this.mMultilineAlignment = multilineAlignment;
+            return this;
+        }
+
+        /** Sets the modifiers of text. */
+        @NonNull
+        Builder setModifiers(@NonNull Modifiers modifiers) {
+            this.mModifiers = modifiers;
+            return this;
+        }
+
+        /**
+         * Sets the overflow for text. If not set, {@link TextAlignment#TEXT_OVERFLOW_TRUNCATE} will
+         * be used.
+         */
+        @NonNull
+        Builder setOverflow(@TextOverflow int overflow) {
+            this.mOverflow = overflow;
+            return this;
+        }
+
+        /** Constructs and returns {@link Text} with the provided content and look. */
+        @NonNull
+        @Override
+        public Text build() {
+            LayoutElementBuilders.Text.Builder text =
+                    new LayoutElementBuilders.Text.Builder()
+                            .setText(mTextContent)
+                            .setFontStyle(
+                                    getFontStyleBuilder(mTypographyName)
+                                            .setColor(mColor)
+                                            .setItalic(mItalic)
+                                            .setUnderline(mUnderline)
+                                            .build())
+                            .setLineHeight(getLineHeightForTypography(mTypographyName))
+                            .setMaxLines(mMaxLines)
+                            .setMultilineAlignment(mMultilineAlignment)
+                            .setModifiers(mModifiers)
+                            .setOverflow(mOverflow);
+            return new Text(text.build());
+        }
+    }
+
+    /** Returns the text of this Text element. */
+    @NonNull
+    public String getText() {
+        return checkNotNull(checkNotNull(mText.getText()).getValue());
+    }
+
+    /** Returns the color of this Text element. */
+    @NonNull
+    public ColorProp getColor() {
+        return checkNotNull(checkNotNull(mText.getFontStyle()).getColor());
+    }
+
+    /** Returns the font style of this Text element. */
+    @NonNull
+    public FontStyle getFontStyle() {
+        return checkNotNull(mText.getFontStyle());
+    }
+
+    /** Returns the line height of this Text element. */
+    public float getLineHeight() {
+        return checkNotNull(mText.getLineHeight()).getValue();
+    }
+
+    /** Returns the max lines of text of this Text element. */
+    public float getMaxLines() {
+        return checkNotNull(mText.getMaxLines()).getValue();
+    }
+
+    /** Returns the multiline alignment of this Text element. */
+    public @TextAlignment int getMultilineAlignment() {
+        return checkNotNull(mText.getMultilineAlignment()).getValue();
+    }
+
+    /** Returns the modifiers of this Text element. */
+    @NonNull
+    public Modifiers getModifiers() {
+        return checkNotNull(mText.getModifiers());
+    }
+
+    /** Returns the overflow of this Text element. */
+    public @TextOverflow int getOverflow() {
+        return checkNotNull(mText.getOverflow()).getValue();
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+        return mText.toLayoutElementProto();
+    }
+}
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/TitleChip.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/TitleChip.java
index 37a565a..7efa8b4 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/TitleChip.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/TitleChip.java
@@ -32,7 +32,6 @@
 import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
 import androidx.wear.tiles.DimensionBuilders.ContainerDimension;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
-import androidx.wear.tiles.LayoutElementBuilders.FontStyles;
 import androidx.wear.tiles.LayoutElementBuilders.HorizontalAlignment;
 import androidx.wear.tiles.LayoutElementBuilders.LayoutElement;
 import androidx.wear.tiles.proto.LayoutElementProto;
@@ -41,7 +40,7 @@
  * Tiles component {@link TitleChip} that represents clickable object with the text.
  *
  * <p>The Title Chip is Stadium shaped object with a larger height then standard Chip and it will
- * take one line of text of {@link FontStyles#title2} style.
+ * take one line of text of {@link Typography#TYPOGRAPHY_TITLE2} style.
  *
  * <p>The recommended set of {@link ChipColors} styles can be obtained from {@link ChipDefaults},
  * e.g. {@link ChipDefaults#TITLE_PRIMARY} to get a color scheme for a primary {@link TitleChip}
@@ -143,7 +142,7 @@
                             .setMaxLines(1)
                             .setHorizontalPadding(TITLE_HORIZONTAL_PADDING)
                             .setPrimaryTextContent(mText)
-                            .setPrimaryTextFontStyle(FontStyles.title2(mDeviceParameters).build());
+                            .setPrimaryTextTypography(Typography.TYPOGRAPHY_TITLE2);
 
             if (mWidth != null) {
                 chipBuilder.setWidth(mWidth);
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Typography.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Typography.java
new file mode 100644
index 0000000..6137980d
--- /dev/null
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Typography.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles.material;
+
+import static androidx.annotation.Dimension.SP;
+import static androidx.wear.tiles.DimensionBuilders.sp;
+import static androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_BOLD;
+import static androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_MEDIUM;
+import static androidx.wear.tiles.LayoutElementBuilders.FONT_WEIGHT_NORMAL;
+import static androidx.wear.tiles.material.Helper.checkNotNull;
+
+import android.annotation.SuppressLint;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.wear.tiles.DimensionBuilders;
+import androidx.wear.tiles.DimensionBuilders.SpProp;
+import androidx.wear.tiles.LayoutElementBuilders.FontStyle;
+import androidx.wear.tiles.LayoutElementBuilders.FontWeight;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Typography styles, currently set up to match Wear's styling. */
+public class Typography {
+    /** Typography for large display text. */
+    public static final int TYPOGRAPHY_DISPLAY1 = 1;
+
+    /** Typography for medium display text. */
+    public static final int TYPOGRAPHY_DISPLAY2 = 2;
+
+    /** Typography for small display text. */
+    public static final int TYPOGRAPHY_DISPLAY3 = 3;
+
+    /** Typography for large title text. */
+    public static final int TYPOGRAPHY_TITLE1 = 4;
+
+    /** Typography for medium title text. */
+    public static final int TYPOGRAPHY_TITLE2 = 5;
+
+    /** Typography for small title text. */
+    public static final int TYPOGRAPHY_TITLE3 = 6;
+
+    /** Typography for large body text. */
+    public static final int TYPOGRAPHY_BODY1 = 7;
+
+    /** Typography for medium body text. */
+    public static final int TYPOGRAPHY_BODY2 = 8;
+
+    /** Typography for bold button text. */
+    public static final int TYPOGRAPHY_BUTTON = 9;
+
+    /** Typography for large caption text. */
+    public static final int TYPOGRAPHY_CAPTION1 = 10;
+
+    /** Typography for medium caption text. */
+    public static final int TYPOGRAPHY_CAPTION2 = 11;
+
+    /** Typography for small caption text. */
+    public static final int TYPOGRAPHY_CAPTION3 = 12;
+
+    /** @hide */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            TYPOGRAPHY_DISPLAY1,
+            TYPOGRAPHY_DISPLAY2,
+            TYPOGRAPHY_DISPLAY3,
+            TYPOGRAPHY_TITLE1,
+            TYPOGRAPHY_TITLE2,
+            TYPOGRAPHY_TITLE3,
+            TYPOGRAPHY_BODY1,
+            TYPOGRAPHY_BODY2,
+            TYPOGRAPHY_BUTTON,
+            TYPOGRAPHY_CAPTION1,
+            TYPOGRAPHY_CAPTION2,
+            TYPOGRAPHY_CAPTION3
+    })
+    public @interface TypographyName {}
+
+    /** Mapping for line height for different typography. */
+    @NonNull
+    private static final Map<Integer, Float> FONT_STYLE_TO_LINE_HEIGHT_SP = new HashMap<>();
+
+    static {
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_DISPLAY1, 46f);
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_DISPLAY2, 40f);
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_DISPLAY3, 36f);
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_TITLE1, 28f);
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_TITLE2, 24f);
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_TITLE3, 20f);
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_BODY1, 20f);
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_BODY2, 18f);
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_BUTTON, 19f);
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_CAPTION1, 18f);
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_CAPTION2, 16f);
+        FONT_STYLE_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_CAPTION3, 14f);
+    }
+
+    private Typography() {}
+
+    /**
+     * Returns the {@link FontStyle.Builder} for the given Typography name with the recommended
+     * size, weight and letter spacing.
+     */
+    @NonNull
+    static FontStyle.Builder getFontStyleBuilder(@TypographyName int typographyCode) {
+        switch (typographyCode) {
+            case TYPOGRAPHY_BODY1:
+                return body1();
+            case TYPOGRAPHY_BODY2:
+                return body2();
+            case TYPOGRAPHY_BUTTON:
+                return button();
+            case TYPOGRAPHY_CAPTION1:
+                return caption1();
+            case TYPOGRAPHY_CAPTION2:
+                return caption2();
+            case TYPOGRAPHY_CAPTION3:
+                return caption3();
+            case TYPOGRAPHY_DISPLAY1:
+                return display1();
+            case TYPOGRAPHY_DISPLAY2:
+                return display2();
+            case TYPOGRAPHY_DISPLAY3:
+                return display3();
+            case TYPOGRAPHY_TITLE1:
+                return title1();
+            case TYPOGRAPHY_TITLE2:
+                return title2();
+            case TYPOGRAPHY_TITLE3:
+                return title3();
+            default:
+                // Shouldn't happen.
+                throw new IllegalArgumentException(
+                        "Typography name " + typographyCode + " doesn't exist.");
+        }
+    }
+
+    /**
+     * Returns the recommended line height for the given Typography to be added to the Text
+     * component.
+     */
+    @NonNull
+    static SpProp getLineHeightForTypography(@TypographyName int typography) {
+        if (!FONT_STYLE_TO_LINE_HEIGHT_SP.containsKey(typography)) {
+            throw new IllegalArgumentException("Typography " + typography + " doesn't exist.");
+        }
+        return sp(checkNotNull(FONT_STYLE_TO_LINE_HEIGHT_SP.get(typography)).intValue());
+    }
+
+    // The @Dimension(unit = SP) on sp() is seemingly being ignored, so lint complains that we're
+    // passing SP to something expecting PX. Just suppress the warning for now.
+    @SuppressLint("ResourceType")
+    private static FontStyle.Builder createFontStyleBuilder(
+            @Dimension(unit = SP) int size, @FontWeight int weight, float letterSpacing) {
+        return new FontStyle.Builder()
+                .setSize(DimensionBuilders.sp(size))
+                .setLetterSpacing(DimensionBuilders.em(letterSpacing))
+                .setWeight(weight);
+    }
+
+    /** Font style for large display text. */
+    @NonNull
+    private static FontStyle.Builder display1() {
+        return createFontStyleBuilder(40, FONT_WEIGHT_MEDIUM, 0.01f);
+    }
+
+    /** Font style for medium display text. */
+    @NonNull
+    private static FontStyle.Builder display2() {
+        return createFontStyleBuilder(34, FONT_WEIGHT_MEDIUM, 0.03f);
+    }
+
+    /** Font style for small display text. */
+    @NonNull
+    private static FontStyle.Builder display3() {
+        return createFontStyleBuilder(30, FONT_WEIGHT_MEDIUM, 0.03f);
+    }
+
+    /** Font style for large title text. */
+    @NonNull
+    private static FontStyle.Builder title1() {
+        return createFontStyleBuilder(24, FONT_WEIGHT_MEDIUM, 0.008f);
+    }
+
+    /** Font style for medium title text. */
+    @NonNull
+    private static FontStyle.Builder title2() {
+        return createFontStyleBuilder(20, FONT_WEIGHT_MEDIUM, 0.01f);
+    }
+
+    /** Font style for small title text. */
+    @NonNull
+    private static FontStyle.Builder title3() {
+        return createFontStyleBuilder(16, FONT_WEIGHT_MEDIUM, 0.01f);
+    }
+
+    /** Font style for normal body text. */
+    @NonNull
+    private static FontStyle.Builder body1() {
+        return createFontStyleBuilder(16, FONT_WEIGHT_NORMAL, 0.01f);
+    }
+
+    /** Font style for small body text. */
+    @NonNull
+    private static FontStyle.Builder body2() {
+        return createFontStyleBuilder(14, FONT_WEIGHT_NORMAL, 0.014f);
+    }
+
+    /** Font style for bold button text. */
+    @NonNull
+    private static FontStyle.Builder button() {
+        return createFontStyleBuilder(15, FONT_WEIGHT_BOLD, 0.03f);
+    }
+
+    /** Font style for large caption text. */
+    @NonNull
+    private static FontStyle.Builder caption1() {
+        return createFontStyleBuilder(14, FONT_WEIGHT_MEDIUM, 0.01f);
+    }
+
+    /** Font style for medium caption text. */
+    @NonNull
+    private static FontStyle.Builder caption2() {
+        return createFontStyleBuilder(12, FONT_WEIGHT_MEDIUM, 0.01f);
+    }
+
+    /** Font style for small caption text. */
+    @NonNull
+    private static FontStyle.Builder caption3() {
+        return createFontStyleBuilder(10, FONT_WEIGHT_MEDIUM, 0.01f);
+    }
+}
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/LayoutDefaults.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/LayoutDefaults.java
index b482f48..c7c132a 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/LayoutDefaults.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/LayoutDefaults.java
@@ -25,26 +25,24 @@
     private LayoutDefaults() {}
 
     /**
-     * The default percentage for the bottom margin for primary chip in the {@link
-     * PrimaryLayout}.
+     * The default percentage for the bottom margin for primary chip in the {@link PrimaryLayout}.
      */
     static final float PRIMARY_LAYOUT_MARGIN_BOTTOM_ROUND_PERCENT = 6.3f / 100;
 
     /**
-     * The default percentage for the bottom margin for primary chip in the {@link
-     * PrimaryLayout}.
+     * The default percentage for the bottom margin for primary chip in the {@link PrimaryLayout}.
      */
     static final float PRIMARY_LAYOUT_MARGIN_BOTTOM_SQUARE_PERCENT = 2.2f / 100;
 
     /**
-     * The default percentage for the top margin for primary chip in the {@link PrimaryLayout}
-     * on round devices.
+     * The default percentage for the top margin for primary chip in the {@link PrimaryLayout} on
+     * round devices.
      */
     static final float PRIMARY_LAYOUT_MARGIN_TOP_ROUND_PERCENT = 16.7f / 100;
 
     /**
-     * The default percentage for the top margin for primary chip in the {@link PrimaryLayout}
-     * on square devices.
+     * The default percentage for the top margin for primary chip in the {@link PrimaryLayout} on
+     * square devices.
      */
     static final float PRIMARY_LAYOUT_MARGIN_TOP_SQUARE_PERCENT = 15.6f / 100;
 
@@ -81,9 +79,9 @@
      */
     public static final float PROGRESS_INDICATOR_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP = 8;
 
-    /** The default spacer width for slots in the {@link MultiSlotLayout}. */
+    /** The default spacer width for slots in a {@link MultiSlotLayout}. */
     public static final DpProp MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH = dp(8);
 
-    /** The recommended space between slots in the {@link MultiSlotLayout} and additional labels. */
+    /** The recommended space between slots in a {@link MultiSlotLayout} and additional labels. */
     public static final DpProp MULTI_SLOT_LAYOUT_VERTICAL_SPACER_HEIGHT = dp(8);
 }
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java
index f7ddb6d..a71012c 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java
@@ -75,10 +75,9 @@
         @NonNull private DpProp mVerticalSpacerHeight = MULTI_SLOT_LAYOUT_VERTICAL_SPACER_HEIGHT;
 
         /**
-         * Creates a builder for the {@link MultiSlotLayout} from the given content. Custom content
-         * inside of it can later be set with {@link #addSlotContent}, {@link
-         * #setPrimaryChipContent}, {@link #setPrimaryLabelTextContent}, {@link
-         * #setSecondaryLabelTextContent}.
+         * Creates a builder for the {@link MultiSlotLayout}. Content inside of it can later be set
+         * with {@link #addSlotContent}, {@link #setPrimaryChipContent}, {@link
+         * #setPrimaryLabelTextContent} and {@link #setSecondaryLabelTextContent}.
          */
         public Builder(@NonNull DeviceParameters deviceParameters) {
             this.mDeviceParameters = deviceParameters;
@@ -123,14 +122,14 @@
         // There is no direct matching getter for this setter as the serialized format of the
         // ProtoLayouts do not allow for a direct reconstruction of the arguments. Instead there are
         // methods to get the contents a whole for rendering.
-        public Builder addSlotContent(@NonNull LayoutElement slotsContent) {
-            mSlotsContent.add(slotsContent);
+        public Builder addSlotContent(@NonNull LayoutElement slotContent) {
+            mSlotsContent.add(slotContent);
             return this;
         }
 
         /**
-         * Sets the horizontal spacer width which is used as a space between slots if there is
-         * more than one slot. If not set, {@link
+         * Sets the horizontal spacer width which is used as a space between slots if there is more
+         * than one slot. If not set, {@link
          * LayoutDefaults#MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH} will be used.
          */
         @NonNull
@@ -144,8 +143,8 @@
         }
 
         /**
-         * Sets the vertical spacer height which is used as a space between all slots and primary
-         * or secondary label if there is any. If not set, {@link
+         * Sets the vertical spacer height which is used as a space between all slots and primary or
+         * secondary label if there is any. If not set, {@link
          * LayoutDefaults#MULTI_SLOT_LAYOUT_VERTICAL_SPACER_HEIGHT} will be used.
          */
         @NonNull
@@ -161,8 +160,7 @@
         @NonNull
         @Override
         public MultiSlotLayout build() {
-            PrimaryLayout.Builder layoutBuilder =
-                    new PrimaryLayout.Builder(mDeviceParameters);
+            PrimaryLayout.Builder layoutBuilder = new PrimaryLayout.Builder(mDeviceParameters);
 
             if (mPrimaryChip != null) {
                 layoutBuilder.setCompactChipContent(mPrimaryChip);
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java
index 58c2652..f0766ed 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java
@@ -50,7 +50,7 @@
 
 /**
  * Tiles layout that represents a suggested layout style for Material Tiles with the primary
- * (compact) chip at the bottom with the given content in a center and the recommended margin and
+ * (compact) chip at the bottom with the given content in the center and the recommended margin and
  * padding applied.
  */
 // TODO(b/215323986): Link visuals.
@@ -69,8 +69,8 @@
         @NonNull private LayoutElement mContent = new Box.Builder().build();
 
         /**
-         * Creates a builder for the {@link PrimaryLayout} from the given content. Custom
-         * content inside of it can later be set with ({@link #setContent}.
+         * Creates a builder for the {@link PrimaryLayout} from the given content. Custom content
+         * inside of it can later be set with ({@link #setContent}.
          */
         public Builder(@NonNull DeviceParameters deviceParameters) {
             this.mDeviceParameters = deviceParameters;
@@ -118,8 +118,7 @@
 
             float primaryChipHeight =
                     mPrimaryChip != null
-                            ? (COMPACT_HEIGHT.getValue()
-                                    + PRIMARY_LAYOUT_SPACER_HEIGHT.getValue())
+                            ? (COMPACT_HEIGHT.getValue() + PRIMARY_LAYOUT_SPACER_HEIGHT.getValue())
                             : 0;
 
             DpProp mainContentHeight =
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/ProgressIndicatorLayout.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/ProgressIndicatorLayout.java
index 0497de9..06e41d0 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/ProgressIndicatorLayout.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/ProgressIndicatorLayout.java
@@ -191,7 +191,7 @@
     /** Get the inner content from this layout. */
     @NonNull
     public LayoutElement getContent() {
-        return checkNotNull(((Box) ((Box) mElement).getContents().get(0))).getContents().get(0);
+        return checkNotNull(((Box) ((Box) mElement).getContents().get(0)).getContents().get(0));
     }
 
     /** @hide */
diff --git a/wear/tiles/tiles/api/current.txt b/wear/tiles/tiles/api/current.txt
index d0a67d7..37c11ec 100644
--- a/wear/tiles/tiles/api/current.txt
+++ b/wear/tiles/tiles/api/current.txt
@@ -151,6 +151,7 @@
     method public static androidx.wear.tiles.DimensionBuilders.DegreesProp degrees(float);
     method public static androidx.wear.tiles.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
     method public static androidx.wear.tiles.DimensionBuilders.EmProp em(int);
+    method public static androidx.wear.tiles.DimensionBuilders.EmProp em(float);
     method public static androidx.wear.tiles.DimensionBuilders.ExpandedDimensionProp expand();
     method public static androidx.wear.tiles.DimensionBuilders.SpProp sp(@Dimension(unit=androidx.annotation.Dimension.SP) float);
     method public static androidx.wear.tiles.DimensionBuilders.WrappedDimensionProp wrap();
diff --git a/wear/tiles/tiles/api/public_plus_experimental_current.txt b/wear/tiles/tiles/api/public_plus_experimental_current.txt
index ce5af10..161cb59 100644
--- a/wear/tiles/tiles/api/public_plus_experimental_current.txt
+++ b/wear/tiles/tiles/api/public_plus_experimental_current.txt
@@ -151,6 +151,7 @@
     method public static androidx.wear.tiles.DimensionBuilders.DegreesProp degrees(float);
     method public static androidx.wear.tiles.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
     method public static androidx.wear.tiles.DimensionBuilders.EmProp em(int);
+    method public static androidx.wear.tiles.DimensionBuilders.EmProp em(float);
     method public static androidx.wear.tiles.DimensionBuilders.ExpandedDimensionProp expand();
     method public static androidx.wear.tiles.DimensionBuilders.SpProp sp(@Dimension(unit=androidx.annotation.Dimension.SP) float);
     method public static androidx.wear.tiles.DimensionBuilders.WrappedDimensionProp wrap();
diff --git a/wear/tiles/tiles/api/restricted_current.txt b/wear/tiles/tiles/api/restricted_current.txt
index d0a67d7..37c11ec 100644
--- a/wear/tiles/tiles/api/restricted_current.txt
+++ b/wear/tiles/tiles/api/restricted_current.txt
@@ -151,6 +151,7 @@
     method public static androidx.wear.tiles.DimensionBuilders.DegreesProp degrees(float);
     method public static androidx.wear.tiles.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
     method public static androidx.wear.tiles.DimensionBuilders.EmProp em(int);
+    method public static androidx.wear.tiles.DimensionBuilders.EmProp em(float);
     method public static androidx.wear.tiles.DimensionBuilders.ExpandedDimensionProp expand();
     method public static androidx.wear.tiles.DimensionBuilders.SpProp sp(@Dimension(unit=androidx.annotation.Dimension.SP) float);
     method public static androidx.wear.tiles.DimensionBuilders.WrappedDimensionProp wrap();
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/DimensionBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/DimensionBuilders.java
index fadeb59..3a754fc 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/DimensionBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/DimensionBuilders.java
@@ -53,6 +53,12 @@
         return new EmProp.Builder().setValue(valueEm).build();
     }
 
+    /** Shortcut for building a {@link EmProp} using a measurement in EM. */
+    @NonNull
+    public static EmProp em(float valueEm) {
+        return new EmProp.Builder().setValue(valueEm).build();
+    }
+
     /** Shortcut for building an {@link DegreesProp} using a measurement in degrees. */
     @NonNull
     public static DegreesProp degrees(float valueDegrees) {
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java
index 075b1bc..4d5cc29 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java
@@ -101,6 +101,7 @@
     }
 
     @Nullable
+    @SuppressWarnings("deprecation")
     private Intent buildUpdateBindIntent() {
         Intent bindIntent = new Intent(ACTION_BIND_UPDATE_REQUESTER);
         bindIntent.setPackage(getSysUiPackageName());
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
index b3f0bb5..c66aed6 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
@@ -92,6 +92,7 @@
 
         /** @hide */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @Suppress("DEPRECATION")
         open class ParserProvider {
             // Open to allow testing without having to install the sample app.
             open fun getParser(context: Context, watchFaceName: ComponentName): XmlResourceParser? {
diff --git a/wear/watchface/watchface-complications-data/api/current.txt b/wear/watchface/watchface-complications-data/api/current.txt
index 9ae823c..abfa218 100644
--- a/wear/watchface/watchface-complications-data/api/current.txt
+++ b/wear/watchface/watchface-complications-data/api/current.txt
@@ -2,6 +2,7 @@
 package androidx.wear.watchface.complications.data {
 
   public abstract sealed class ComplicationData {
+    method public java.time.Instant getNextChangeInstant(java.time.Instant afterInstant);
     method public final android.app.PendingIntent? getTapAction();
     method public final androidx.wear.watchface.complications.data.ComplicationType getType();
     method public final androidx.wear.watchface.complications.data.TimeRange getValidTimeRange();
diff --git a/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt b/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
index 9ae823c..abfa218 100644
--- a/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
@@ -2,6 +2,7 @@
 package androidx.wear.watchface.complications.data {
 
   public abstract sealed class ComplicationData {
+    method public java.time.Instant getNextChangeInstant(java.time.Instant afterInstant);
     method public final android.app.PendingIntent? getTapAction();
     method public final androidx.wear.watchface.complications.data.ComplicationType getType();
     method public final androidx.wear.watchface.complications.data.TimeRange getValidTimeRange();
diff --git a/wear/watchface/watchface-complications-data/api/restricted_current.txt b/wear/watchface/watchface-complications-data/api/restricted_current.txt
index bf6d73a..9c89030 100644
--- a/wear/watchface/watchface-complications-data/api/restricted_current.txt
+++ b/wear/watchface/watchface-complications-data/api/restricted_current.txt
@@ -2,6 +2,7 @@
 package androidx.wear.watchface.complications.data {
 
   public abstract sealed class ComplicationData {
+    method public java.time.Instant getNextChangeInstant(java.time.Instant afterInstant);
     method public final android.app.PendingIntent? getTapAction();
     method public final androidx.wear.watchface.complications.data.ComplicationType getType();
     method public final androidx.wear.watchface.complications.data.TimeRange getValidTimeRange();
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
index 1e01a43..4866e2a 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
@@ -75,6 +75,16 @@
         cachedWireComplicationData?.let {
             WireComplicationDataBuilder(it)
         } ?: WireComplicationDataBuilder(type.toWireComplicationType())
+
+    /**
+     * Returns the next [Instant] after [afterInstant] at which any field of the complication may
+     * change. If there's no scheduled changes then [Instant.MAX] will be returned.
+     *
+     * See [ComplicationText.getNextChangeTime]
+     *
+     * @param afterInstant The earliest [Instant] for which we're interested in changes
+     */
+    public open fun getNextChangeInstant(afterInstant: Instant): Instant = Instant.MAX
 }
 
 /**
@@ -308,6 +318,20 @@
             "tapAction=$tapAction, validTimeRange=$validTimeRange)"
     }
 
+    override fun getNextChangeInstant(afterInstant: Instant): Instant {
+        if (title != null) {
+            val titleChangeInstant = title.getNextChangeTime(afterInstant)
+            val textChangeInstant = text.getNextChangeTime(afterInstant)
+            return if (textChangeInstant.isBefore(titleChangeInstant)) {
+                textChangeInstant
+            } else {
+                titleChangeInstant
+            }
+        } else {
+            return text.getNextChangeTime(afterInstant)
+        }
+    }
+
     /** @hide */
     public companion object {
         /** The [ComplicationType] corresponding to objects of this type. */
@@ -469,6 +493,20 @@
             "tapAction=$tapAction, validTimeRange=$validTimeRange)"
     }
 
+    override fun getNextChangeInstant(afterInstant: Instant): Instant {
+        if (title != null) {
+            val titleChangeInstant = title.getNextChangeTime(afterInstant)
+            val textChangeInstant = text.getNextChangeTime(afterInstant)
+            return if (textChangeInstant.isBefore(titleChangeInstant)) {
+                textChangeInstant
+            } else {
+                titleChangeInstant
+            }
+        } else {
+            return text.getNextChangeTime(afterInstant)
+        }
+    }
+
     /** @hide */
     public companion object {
         /** The [ComplicationType] corresponding to objects of this type. */
@@ -640,6 +678,16 @@
             "tapAction=$tapAction, validTimeRange=$validTimeRange)"
     }
 
+    override fun getNextChangeInstant(afterInstant: Instant): Instant {
+        val titleChangeInstant = title?.getNextChangeTime(afterInstant) ?: Instant.MAX
+        val textChangeInstant = text?.getNextChangeTime(afterInstant) ?: Instant.MAX
+        return if (textChangeInstant.isBefore(titleChangeInstant)) {
+            textChangeInstant
+        } else {
+            titleChangeInstant
+        }
+    }
+
     /** @hide */
     public companion object {
         /** The [ComplicationType] corresponding to objects of this type. */
@@ -1137,6 +1185,16 @@
             "validTimeRange=$validTimeRange)"
     }
 
+    override fun getNextChangeInstant(afterInstant: Instant): Instant {
+        val titleChangeInstant = title?.getNextChangeTime(afterInstant) ?: Instant.MAX
+        val textChangeInstant = text?.getNextChangeTime(afterInstant) ?: Instant.MAX
+        return if (textChangeInstant.isBefore(titleChangeInstant)) {
+            textChangeInstant
+        } else {
+            titleChangeInstant
+        }
+    }
+
     /** @hide */
     public companion object {
         /** The [ComplicationType] corresponding to objects of this type. */
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
index 799d9c1..4c88ed9 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
@@ -502,8 +502,14 @@
     override fun returnsSameText(firstInstant: Instant, secondInstant: Instant) =
         delegate.returnsSameText(firstInstant.toEpochMilli(), secondInstant.toEpochMilli())
 
-    override fun getNextChangeTime(afterInstant: Instant) =
-        Instant.ofEpochMilli(delegate.getNextChangeTime(afterInstant.toEpochMilli()))
+    override fun getNextChangeTime(afterInstant: Instant): Instant {
+        val nextChangeTime = delegate.getNextChangeTime(afterInstant.toEpochMilli())
+        return if (nextChangeTime == Long.MAX_VALUE) {
+             Instant.MAX
+        } else {
+            Instant.ofEpochMilli(nextChangeTime)
+        }
+    }
 
     override fun isAlwaysEmpty() = delegate.isAlwaysEmpty
     override fun getTimeDependentText(): TimeDependentText = delegate.timeDependentText
@@ -550,8 +556,14 @@
     override fun returnsSameText(firstInstant: Instant, secondInstant: Instant) =
         delegate.returnsSameText(firstInstant.toEpochMilli(), secondInstant.toEpochMilli())
 
-    override fun getNextChangeTime(afterInstant: Instant) =
-        Instant.ofEpochMilli(delegate.getNextChangeTime(afterInstant.toEpochMilli()))
+    override fun getNextChangeTime(afterInstant: Instant): Instant {
+        val nextChangeTime = delegate.getNextChangeTime(afterInstant.toEpochMilli())
+        return if (nextChangeTime == Long.MAX_VALUE) {
+            Instant.MAX
+        } else {
+            Instant.ofEpochMilli(nextChangeTime)
+        }
+    }
 
     override fun isAlwaysEmpty() = false
 
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/TextTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/TextTest.kt
index d792341..b691df3 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/TextTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/TextTest.kt
@@ -134,10 +134,8 @@
         val wireText = WireComplicationText.plainText("abc")
         val text = wireText.toApiComplicationText()
 
-        assertThat(text.getTextAt(getResource(), Instant.EPOCH))
-            .isEqualTo("abc")
-        assertThat(text.getNextChangeTime(Instant.EPOCH))
-            .isEqualTo(Instant.ofEpochMilli(Long.MAX_VALUE))
+        assertThat(text.getTextAt(getResource(), Instant.EPOCH)).isEqualTo("abc")
+        assertThat(text.getNextChangeTime(Instant.EPOCH)).isEqualTo(Instant.MAX)
         assertThat(text.isAlwaysEmpty()).isFalse()
         assertThat(
             text.returnsSameText(
diff --git a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
index 94a2460..0c2efd9 100644
--- a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
+++ b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
@@ -2309,6 +2309,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION")
     public fun watchfaceSupportsHeadlessEditing() {
         val mockPackageManager = Mockito.mock(PackageManager::class.java)
 
@@ -2331,6 +2332,7 @@
     }
 
     @Test
+    @Suppress("DEPRECATION")
     public fun watchfaceSupportsHeadlessEditing_oldApi() {
         val mockPackageManager = Mockito.mock(PackageManager::class.java)
 
diff --git a/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/WatchFaceEditorContract.kt b/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/WatchFaceEditorContract.kt
index 22dd404..1fead73 100644
--- a/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/WatchFaceEditorContract.kt
+++ b/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/WatchFaceEditorContract.kt
@@ -186,6 +186,7 @@
          */
         @JvmStatic
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @Suppress("DEPRECATION")
         @Throws(PackageManager.NameNotFoundException::class)
         public fun supportsWatchFaceHeadlessEditing(
             packageManager: PackageManager,
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt
index 53b905d..c183430 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt
@@ -438,4 +438,22 @@
                     .systemDataSourceFallbackDefaultType.toWireComplicationType()
             )
         }.toTypedArray()
+
+    /**
+     * Returns the earliest [Instant] after [afterInstant] at which any complication field in any
+     * enabled complication may change.
+     */
+    internal fun getNextChangeInstant(afterInstant: Instant): Instant {
+        var minInstant = Instant.MAX
+        for ((_, complication) in complicationSlots) {
+            if (!complication.enabled) {
+                continue
+            }
+            val instant = complication.complicationData.value.getNextChangeInstant(afterInstant)
+            if (instant.isBefore(minInstant)) {
+                minInstant = instant
+            }
+        }
+        return minInstant
+    }
 }
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index 0f500b2..006be96 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -56,6 +56,7 @@
 import androidx.wear.watchface.style.UserStyleSchema
 import androidx.wear.watchface.style.WatchFaceLayer
 import androidx.wear.watchface.utility.TraceEvent
+import java.lang.Long.min
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -801,7 +802,8 @@
         // an image. However if we're animating there's no need to trigger an extra invalidation.
         if (!renderer.shouldAnimate() || computeDelayTillNextFrame(
                 nextDrawTimeMillis,
-                systemTimeProvider.getSystemTimeMillis()
+                systemTimeProvider.getSystemTimeMillis(),
+                Instant.now()
             ) > MIN_PERCEPTIBLE_DELAY_MILLIS
         ) {
             watchFaceHostApi.invalidate()
@@ -976,7 +978,7 @@
     internal fun onDraw() {
         val startTime = getZonedDateTime()
         val startInstant = startTime.toInstant()
-        val startTimeMillis = startInstant.toEpochMilli()
+        val startTimeMillis = systemTimeProvider.getSystemTimeMillis()
         maybeUpdateDrawMode()
         complicationSlotsManager.selectComplicationDataForInstant(startInstant)
         renderer.renderInternal(startTime)
@@ -984,7 +986,7 @@
 
         if (renderer.shouldAnimate()) {
             val currentTimeMillis = systemTimeProvider.getSystemTimeMillis()
-            var delay = computeDelayTillNextFrame(startTimeMillis, currentTimeMillis)
+            var delay = computeDelayTillNextFrame(startTimeMillis, currentTimeMillis, Instant.now())
             nextDrawTimeMillis = currentTimeMillis + delay
 
             // We want to post our delayed task to post the choreographer frame a bit earlier than
@@ -1005,11 +1007,18 @@
         renderer.renderInternal(getZonedDateTime())
     }
 
-    /** @hide */
+    /**
+     * @param startTimeMillis The SystemTime in milliseconds at which we started rendering
+     * @param currentTimeMillis The current SystemTime in milliseconds
+     * @param nowInstant The current [Instant].
+     *
+     * @hide
+     */
     @UiThread
     internal fun computeDelayTillNextFrame(
         startTimeMillis: Long,
-        currentTimeMillis: Long
+        currentTimeMillis: Long,
+        nowInstant: Instant
     ): Long {
         // Limit update rate to conserve power when the battery is low and not charging.
         val updateRateMillis =
@@ -1049,7 +1058,18 @@
             nextFrameTimeMillis += (60000 - (nextFrameTimeMillis % 60000)) % 60000
         }
 
-        return nextFrameTimeMillis - currentTimeMillis
+        var delayMillis = nextFrameTimeMillis - currentTimeMillis
+
+        // Check if we need to render a frame sooner to support scheduled complication updates, e.g.
+        // the stop watch complication.
+        val nextComplicationChange = complicationSlotsManager.getNextChangeInstant(nowInstant)
+        if (nextComplicationChange != Instant.MAX) {
+            val nextComplicationChangeDelayMillis =
+                max(0, nextComplicationChange.toEpochMilli() - nowInstant.toEpochMilli())
+            delayMillis = min(delayMillis, nextComplicationChangeDelayMillis)
+        }
+
+        return delayMillis
     }
 
     /**
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index 2fd05d3..35de627 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -312,6 +312,7 @@
      * @hide
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Suppress("DEPRECATION")
     public open fun getXmlWatchFaceResourceId(): Int {
         return try {
             packageManager.getServiceInfo(
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index 11b33f0..813bdc0 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -51,10 +51,13 @@
 import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
 import androidx.wear.watchface.complications.SystemDataSources
 import androidx.wear.watchface.complications.data.ComplicationType
+import androidx.wear.watchface.complications.data.CountUpTimeReference
 import androidx.wear.watchface.complications.data.EmptyComplicationData
 import androidx.wear.watchface.complications.data.NoDataComplicationData
 import androidx.wear.watchface.complications.data.PlainComplicationText
 import androidx.wear.watchface.complications.data.ShortTextComplicationData
+import androidx.wear.watchface.complications.data.TimeDifferenceComplicationText
+import androidx.wear.watchface.complications.data.TimeDifferenceStyle
 import androidx.wear.watchface.complications.data.toApiComplicationData
 import androidx.wear.watchface.complications.rendering.CanvasComplicationDrawable
 import androidx.wear.watchface.complications.rendering.ComplicationDrawable
@@ -114,6 +117,7 @@
 import java.time.ZonedDateTime
 import java.util.ArrayDeque
 import java.util.PriorityQueue
+import java.util.concurrent.TimeUnit
 import kotlin.test.assertFailsWith
 
 private const val INTERACTIVE_UPDATE_RATE_MS = 16L
@@ -1066,7 +1070,7 @@
             UserStyleSchema(emptyList())
         )
 
-        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0)).isEqualTo(
+        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0, Instant.EPOCH)).isEqualTo(
             INTERACTIVE_UPDATE_RATE_MS
         )
 
@@ -1075,7 +1079,7 @@
             context,
             Intent(Intent.ACTION_BATTERY_LOW)
         )
-        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0)).isEqualTo(
+        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0, Instant.EPOCH)).isEqualTo(
             WatchFaceImpl.MAX_LOW_POWER_INTERACTIVE_UPDATE_RATE_MS
         )
 
@@ -1084,7 +1088,7 @@
             context,
             Intent(Intent.ACTION_BATTERY_OKAY)
         )
-        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0)).isEqualTo(
+        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0, Instant.EPOCH)).isEqualTo(
             INTERACTIVE_UPDATE_RATE_MS
         )
     }
@@ -1097,7 +1101,7 @@
             UserStyleSchema(emptyList())
         )
 
-        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0)).isEqualTo(
+        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0, Instant.EPOCH)).isEqualTo(
             INTERACTIVE_UPDATE_RATE_MS
         )
 
@@ -1106,7 +1110,7 @@
             context,
             Intent(Intent.ACTION_BATTERY_LOW)
         )
-        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0)).isEqualTo(
+        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0, Instant.EPOCH)).isEqualTo(
             WatchFaceImpl.MAX_LOW_POWER_INTERACTIVE_UPDATE_RATE_MS
         )
 
@@ -1115,7 +1119,7 @@
             context,
             Intent(Intent.ACTION_POWER_CONNECTED)
         )
-        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0)).isEqualTo(
+        assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0, Instant.EPOCH)).isEqualTo(
             INTERACTIVE_UPDATE_RATE_MS
         )
     }
@@ -1131,7 +1135,8 @@
         assertThat(
             watchFaceImpl.computeDelayTillNextFrame(
                 startTimeMillis = 0,
-                currentTimeMillis = 2
+                currentTimeMillis = 2,
+                Instant.EPOCH
             )
         )
             .isEqualTo(INTERACTIVE_UPDATE_RATE_MS - 2)
@@ -1149,7 +1154,8 @@
         assertThat(
             watchFaceImpl.computeDelayTillNextFrame(
                 startTimeMillis = 2,
-                currentTimeMillis = INTERACTIVE_UPDATE_RATE_MS + 3
+                currentTimeMillis = INTERACTIVE_UPDATE_RATE_MS + 3,
+                Instant.EPOCH
             )
         ).isEqualTo(- 1)
     }
@@ -1168,7 +1174,8 @@
         assertThat(
             watchFaceImpl.computeDelayTillNextFrame(
                 startTimeMillis = 20,
-                currentTimeMillis = 24
+                currentTimeMillis = 24,
+                Instant.EPOCH
             )
         ).isEqualTo(INTERACTIVE_UPDATE_RATE_MS - 4)
     }
@@ -1188,7 +1195,8 @@
         assertThat(
             watchFaceImpl.computeDelayTillNextFrame(
                 startTimeMillis = 100740,
-                currentTimeMillis = 100750
+                currentTimeMillis = 100750,
+                Instant.EPOCH
             )
         ).isEqualTo(250)
     }
@@ -1208,7 +1216,8 @@
         assertThat(
             watchFaceImpl.computeDelayTillNextFrame(
                 startTimeMillis = 10000,
-                currentTimeMillis = 10001
+                currentTimeMillis = 10001,
+                Instant.EPOCH
             )
         ).isEqualTo(999)
     }
@@ -1224,13 +1233,93 @@
         renderer.interactiveDrawModeUpdateDelayMillis = 60000
 
         // Simulate rendering 2s into a minute, after which we should delay till the next minute.
-        watchFaceImpl.nextDrawTimeMillis = 60000 + (120)
+        watchFaceImpl.nextDrawTimeMillis = 60000 + 2000
         assertThat(
             watchFaceImpl.computeDelayTillNextFrame(
                 startTimeMillis = watchFaceImpl.nextDrawTimeMillis,
-                currentTimeMillis = watchFaceImpl.nextDrawTimeMillis
+                currentTimeMillis = watchFaceImpl.nextDrawTimeMillis,
+                Instant.EPOCH
             )
-        ).isEqualTo(59880) // NB 59880 + 120 == 60000
+        ).isEqualTo(58000) // NB 58000 + 2000 == 60000
+    }
+
+    @Test
+    public fun computeDelayTillNextFrame_60000ms_update_with_stopwatchComplication() {
+        initEngine(
+            WatchFaceType.ANALOG,
+            listOf(leftComplication, rightComplication),
+            UserStyleSchema(emptyList())
+        )
+
+        watchFaceImpl.onComplicationSlotDataUpdate(
+            LEFT_COMPLICATION_ID,
+            ShortTextComplicationData.Builder(
+                TimeDifferenceComplicationText.Builder(
+                    TimeDifferenceStyle.STOPWATCH,
+                    CountUpTimeReference(Instant.parse("2022-10-30T10:15:30.001Z"))
+                ).setMinimumTimeUnit(TimeUnit.MINUTES).build(),
+                androidx.wear.watchface.complications.data.ComplicationText.EMPTY
+            ).build()
+        )
+
+        renderer.interactiveDrawModeUpdateDelayMillis = 60000
+
+        // Simulate rendering 2s into a minute of system time, normally we'd need to wait 58 seconds
+        // but the complication needs an update in 50s so our delay is shorter.
+        watchFaceImpl.nextDrawTimeMillis = 60000 + 2000
+        assertThat(
+            watchFaceImpl.computeDelayTillNextFrame(
+                startTimeMillis = watchFaceImpl.nextDrawTimeMillis,
+                currentTimeMillis = watchFaceImpl.nextDrawTimeMillis,
+                Instant.EPOCH.plusSeconds(10)
+            )
+        ).isEqualTo(50001)
+    }
+
+    @Test
+    public fun complicationSlotsManager_getNextChangeInstant() {
+        initEngine(
+            WatchFaceType.ANALOG,
+            listOf(leftComplication, rightComplication),
+            UserStyleSchema(emptyList())
+        )
+
+        // Initially neither complication has a scheduled change.
+        assertThat(complicationSlotsManager.getNextChangeInstant(Instant.EPOCH))
+            .isEqualTo(Instant.MAX)
+
+        // Sending a complication with a scheduled update alters the result of getNextChangeInstant.
+        val referenceInstant = Instant.parse("2022-10-30T10:15:30.001Z")
+        watchFaceImpl.onComplicationSlotDataUpdate(
+            LEFT_COMPLICATION_ID,
+            ShortTextComplicationData.Builder(
+                TimeDifferenceComplicationText.Builder(
+                    TimeDifferenceStyle.STOPWATCH,
+                    CountUpTimeReference(referenceInstant)
+                ).setMinimumTimeUnit(TimeUnit.HOURS).build(),
+                androidx.wear.watchface.complications.data.ComplicationText.EMPTY
+            ).build()
+        )
+
+        val nowInstant = Instant.EPOCH.plusSeconds(10)
+        assertThat(complicationSlotsManager.getNextChangeInstant(nowInstant))
+            .isEqualTo(Instant.EPOCH.plusSeconds(60 * 60).plusMillis(1))
+
+        // Sending another complication with an earlier scheduled update alters the result of
+        // getNextChangeInstant again.
+        watchFaceImpl.onComplicationSlotDataUpdate(
+            RIGHT_COMPLICATION_ID,
+            ShortTextComplicationData.Builder(
+                TimeDifferenceComplicationText.Builder(
+                    TimeDifferenceStyle.STOPWATCH,
+                    CountUpTimeReference(referenceInstant)
+                ).setMinimumTimeUnit(TimeUnit.SECONDS).build(),
+                androidx.wear.watchface.complications.data.ComplicationText.EMPTY
+            ).build()
+        )
+
+        assertThat(complicationSlotsManager.getNextChangeInstant(nowInstant))
+            .isEqualTo(Instant.EPOCH.plusSeconds(10).plusMillis(1))
     }
 
     @Test
diff --git a/wear/wear/src/main/java/androidx/wear/utils/MetadataConstants.java b/wear/wear/src/main/java/androidx/wear/utils/MetadataConstants.java
index af5400e..e8f2716 100644
--- a/wear/wear/src/main/java/androidx/wear/utils/MetadataConstants.java
+++ b/wear/wear/src/main/java/androidx/wear/utils/MetadataConstants.java
@@ -130,6 +130,7 @@
      * @param context to be evaluated.
      * @return Whether a given context comes from a standalone app.
      */
+    @SuppressWarnings("deprecation")
     public static boolean isStandalone(Context context) {
         try {
             ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
@@ -150,6 +151,7 @@
      * @param context to be evaluated.
      * @return Whether a given context has notification bridging enabled.
      */
+    @SuppressWarnings("deprecation")
     public static boolean isNotificationBridgingEnabled(Context context) {
         try {
             ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
@@ -173,6 +175,7 @@
      * @return an integer id representing the resource id of the requested drawable, or 0 if
      * no drawable was found.
      */
+    @SuppressWarnings("deprecation")
     public static int getPreviewDrawableResourceId(Context context, boolean circular) {
         try {
             ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
index 01a3a1a..5bfc430 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
@@ -384,6 +384,7 @@
      * if WebView was to be loaded right now.
      */
     @SuppressLint("PrivateApi")
+    @SuppressWarnings("deprecation")
     private static PackageInfo getNotYetLoadedWebViewPackageInfo(Context context) {
         String webviewPackageName;
         try {
diff --git a/work/integration-tests/testapp/src/main/AndroidManifest.xml b/work/integration-tests/testapp/src/main/AndroidManifest.xml
index c6d608d..17ab7d6 100644
--- a/work/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/work/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -49,17 +49,20 @@
         <service
             android:name="androidx.work.multiprocess.RemoteWorkerService"
             android:exported="false"
-            android:process=":worker1" />
+            android:process=":worker1"
+            tools:ignore="MissingServiceExportedEqualsTrue" />
 
         <service
             android:name=".RemoteWorkerService2"
             android:exported="false"
-            android:process=":worker2" />
+            android:process=":worker2"
+            tools:ignore="MissingServiceExportedEqualsTrue" />
 
         <service
             android:name=".RemoteService"
             android:exported="false"
-            android:process=":remote" />
+            android:process=":remote"
+            tools:ignore="MissingServiceExportedEqualsTrue" />
     </application>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 </manifest>
diff --git a/work/work-gcm/src/main/AndroidManifest.xml b/work/work-gcm/src/main/AndroidManifest.xml
index de6f154..91c1375 100644
--- a/work/work-gcm/src/main/AndroidManifest.xml
+++ b/work/work-gcm/src/main/AndroidManifest.xml
@@ -24,7 +24,8 @@
             android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE"
             android:exported="@bool/enable_gcm_scheduler_default"
             android:directBootAware="false"
-            tools:targetApi="n">
+            tools:targetApi="n"
+            tools:ignore="MissingServiceExportedEqualsTrue">
             <intent-filter>
                 <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY"/>
             </intent-filter>
diff --git a/work/work-multiprocess/src/main/AndroidManifest.xml b/work/work-multiprocess/src/main/AndroidManifest.xml
index 4c38d87..855611d 100644
--- a/work/work-multiprocess/src/main/AndroidManifest.xml
+++ b/work/work-multiprocess/src/main/AndroidManifest.xml
@@ -13,13 +13,16 @@
   ~ 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.work.multiprocess">
+<manifest
+    package="androidx.work.multiprocess"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" >
 
     <application>
         <service
             android:name=".RemoteWorkManagerService"
-            android:exported="false" />
+            android:exported="false"
+            tools:ignore="MissingServiceExportedEqualsTrue" />
     </application>
 
 </manifest>
diff --git a/work/work-runtime/src/main/AndroidManifest.xml b/work/work-runtime/src/main/AndroidManifest.xml
index c772c5e..f8cc60a 100644
--- a/work/work-runtime/src/main/AndroidManifest.xml
+++ b/work/work-runtime/src/main/AndroidManifest.xml
@@ -37,7 +37,8 @@
             android:exported="false"
             android:enabled="@bool/enable_system_alarm_service_default"
             android:directBootAware="false"
-            tools:targetApi="n"/>
+            tools:targetApi="n"
+            tools:ignore="MissingServiceExportedEqualsTrue" />
         <service
             android:name="androidx.work.impl.background.systemjob.SystemJobService"
             android:permission="android.permission.BIND_JOB_SERVICE"
@@ -50,7 +51,8 @@
             android:exported="false"
             android:directBootAware="false"
             android:enabled="@bool/enable_system_foreground_service_default"
-            tools:targetApi="n"/>
+            tools:targetApi="n"
+            tools:ignore="MissingServiceExportedEqualsTrue" />
         <receiver
             android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
             android:enabled="true"