Merge "Fix modifiers of properties & accessors" into androidx-master-dev
diff --git a/activity/activity/src/main/java/androidx/activity/result/ActivityResultLauncher.java b/activity/activity/src/main/java/androidx/activity/result/ActivityResultLauncher.java
index 84aa1bf..b903221 100644
--- a/activity/activity/src/main/java/androidx/activity/result/ActivityResultLauncher.java
+++ b/activity/activity/src/main/java/androidx/activity/result/ActivityResultLauncher.java
@@ -36,7 +36,12 @@
     /**
      * Executes an {@link ActivityResultContract}.
      *
+     * <p>This method throws {@link android.content.ActivityNotFoundException}
+     * if there was no Activity found to run the given Intent.
+
      * @param input the input required to execute an {@link ActivityResultContract}.
+     *
+     * @throws android.content.ActivityNotFoundException
      */
     public void launch(@SuppressLint("UnknownNullness") I input) {
         launch(input, null);
@@ -45,8 +50,13 @@
     /**
      * Executes an {@link ActivityResultContract}.
      *
+     * <p>This method throws {@link android.content.ActivityNotFoundException}
+     * if there was no Activity found to run the given Intent.
+     *
      * @param input the input required to execute an {@link ActivityResultContract}.
      * @param options Additional options for how the Activity should be started.
+     *
+     * @throws android.content.ActivityNotFoundException
      */
     public abstract void launch(@SuppressLint("UnknownNullness") I input,
             @Nullable ActivityOptionsCompat options);
diff --git a/appsearch/appsearch/api/current.txt b/appsearch/appsearch/api/current.txt
index 8cfd64d..0670b58 100644
--- a/appsearch/appsearch/api/current.txt
+++ b/appsearch/appsearch/api/current.txt
@@ -54,7 +54,7 @@
 
   public final class AppSearchSchema {
     method public java.util.List<androidx.appsearch.app.AppSearchSchema.PropertyConfig!> getProperties();
-    method public String getSchemaTypeName();
+    method public String getSchemaType();
   }
 
   public static final class AppSearchSchema.Builder {
@@ -226,7 +226,7 @@
     method public int getNumPerPage();
     method public int getOrder();
     method public int getRankingStrategy();
-    method public java.util.List<java.lang.String!> getSchemas();
+    method public java.util.List<java.lang.String!> getSchemaTypes();
     method public int getSnippetCount();
     method public int getSnippetCountPerProperty();
     method public int getTermMatch();
@@ -243,10 +243,10 @@
     ctor public SearchSpec.Builder();
     method public androidx.appsearch.app.SearchSpec.Builder addNamespace(java.lang.String!...);
     method public androidx.appsearch.app.SearchSpec.Builder addNamespace(java.util.Collection<java.lang.String!>);
-    method public androidx.appsearch.app.SearchSpec.Builder addSchema(java.lang.String!...);
-    method public androidx.appsearch.app.SearchSpec.Builder addSchema(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec.Builder addSchemaByDataClass(java.util.Collection<java.lang.Class<?>!>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SearchSpec.Builder addSchemaByDataClass(Class<?>!...) throws androidx.appsearch.exceptions.AppSearchException;
+    method public androidx.appsearch.app.SearchSpec.Builder addSchemaType(java.lang.String!...);
+    method public androidx.appsearch.app.SearchSpec.Builder addSchemaType(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec build();
     method public androidx.appsearch.app.SearchSpec.Builder setMaxSnippetSize(int);
     method public androidx.appsearch.app.SearchSpec.Builder setNumPerPage(int);
diff --git a/appsearch/appsearch/api/public_plus_experimental_current.txt b/appsearch/appsearch/api/public_plus_experimental_current.txt
index 8cfd64d..0670b58 100644
--- a/appsearch/appsearch/api/public_plus_experimental_current.txt
+++ b/appsearch/appsearch/api/public_plus_experimental_current.txt
@@ -54,7 +54,7 @@
 
   public final class AppSearchSchema {
     method public java.util.List<androidx.appsearch.app.AppSearchSchema.PropertyConfig!> getProperties();
-    method public String getSchemaTypeName();
+    method public String getSchemaType();
   }
 
   public static final class AppSearchSchema.Builder {
@@ -226,7 +226,7 @@
     method public int getNumPerPage();
     method public int getOrder();
     method public int getRankingStrategy();
-    method public java.util.List<java.lang.String!> getSchemas();
+    method public java.util.List<java.lang.String!> getSchemaTypes();
     method public int getSnippetCount();
     method public int getSnippetCountPerProperty();
     method public int getTermMatch();
@@ -243,10 +243,10 @@
     ctor public SearchSpec.Builder();
     method public androidx.appsearch.app.SearchSpec.Builder addNamespace(java.lang.String!...);
     method public androidx.appsearch.app.SearchSpec.Builder addNamespace(java.util.Collection<java.lang.String!>);
-    method public androidx.appsearch.app.SearchSpec.Builder addSchema(java.lang.String!...);
-    method public androidx.appsearch.app.SearchSpec.Builder addSchema(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec.Builder addSchemaByDataClass(java.util.Collection<java.lang.Class<?>!>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SearchSpec.Builder addSchemaByDataClass(Class<?>!...) throws androidx.appsearch.exceptions.AppSearchException;
+    method public androidx.appsearch.app.SearchSpec.Builder addSchemaType(java.lang.String!...);
+    method public androidx.appsearch.app.SearchSpec.Builder addSchemaType(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec build();
     method public androidx.appsearch.app.SearchSpec.Builder setMaxSnippetSize(int);
     method public androidx.appsearch.app.SearchSpec.Builder setNumPerPage(int);
diff --git a/appsearch/appsearch/api/restricted_current.txt b/appsearch/appsearch/api/restricted_current.txt
index 8cfd64d..0670b58 100644
--- a/appsearch/appsearch/api/restricted_current.txt
+++ b/appsearch/appsearch/api/restricted_current.txt
@@ -54,7 +54,7 @@
 
   public final class AppSearchSchema {
     method public java.util.List<androidx.appsearch.app.AppSearchSchema.PropertyConfig!> getProperties();
-    method public String getSchemaTypeName();
+    method public String getSchemaType();
   }
 
   public static final class AppSearchSchema.Builder {
@@ -226,7 +226,7 @@
     method public int getNumPerPage();
     method public int getOrder();
     method public int getRankingStrategy();
-    method public java.util.List<java.lang.String!> getSchemas();
+    method public java.util.List<java.lang.String!> getSchemaTypes();
     method public int getSnippetCount();
     method public int getSnippetCountPerProperty();
     method public int getTermMatch();
@@ -243,10 +243,10 @@
     ctor public SearchSpec.Builder();
     method public androidx.appsearch.app.SearchSpec.Builder addNamespace(java.lang.String!...);
     method public androidx.appsearch.app.SearchSpec.Builder addNamespace(java.util.Collection<java.lang.String!>);
-    method public androidx.appsearch.app.SearchSpec.Builder addSchema(java.lang.String!...);
-    method public androidx.appsearch.app.SearchSpec.Builder addSchema(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec.Builder addSchemaByDataClass(java.util.Collection<java.lang.Class<?>!>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SearchSpec.Builder addSchemaByDataClass(Class<?>!...) throws androidx.appsearch.exceptions.AppSearchException;
+    method public androidx.appsearch.app.SearchSpec.Builder addSchemaType(java.lang.String!...);
+    method public androidx.appsearch.app.SearchSpec.Builder addSchemaType(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec build();
     method public androidx.appsearch.app.SearchSpec.Builder setMaxSnippetSize(int);
     method public androidx.appsearch.app.SearchSpec.Builder setNumPerPage(int);
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java
index 09ac4df..04ae3f3 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java
@@ -279,7 +279,7 @@
         SearchResults searchResults = mSession.query("",
                 new SearchSpec.Builder()
                         .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .addSchema("Gift", AppSearchEmail.SCHEMA_TYPE)
+                        .addSchemaType("Gift", AppSearchEmail.SCHEMA_TYPE)
                         .build());
         List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
         assertThat(documents).hasSize(3);
@@ -297,7 +297,7 @@
         searchResults = mSession.query("",
                 new SearchSpec.Builder()
                         .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .addSchema(AppSearchEmail.SCHEMA_TYPE)
+                        .addSchemaType(AppSearchEmail.SCHEMA_TYPE)
                         .addSchemaByDataClass(Gift.class)
                         .build());
         documents = convertSearchResultsToDocuments(searchResults);
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java
index 48129c8..d97a706 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java
@@ -516,7 +516,7 @@
 
         // Query only for Document
         searchResults = mDb1.query("body", new SearchSpec.Builder()
-                .addSchema("Generic", "Generic") // duplicate type in filter won't matter.
+                .addSchemaType("Generic", "Generic") // duplicate type in filter won't matter.
                 .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                 .build());
         documents = convertSearchResultsToDocuments(searchResults);
@@ -647,7 +647,7 @@
         // Query for the document
         SearchResults searchResults = mDb1.query("foo",
                 new SearchSpec.Builder()
-                        .addSchema("Generic")
+                        .addSchemaType("Generic")
                         .setSnippetCount(1)
                         .setSnippetCountPerProperty(1)
                         .setMaxSnippetSize(10)
@@ -851,7 +851,7 @@
         checkIsResultSuccess(mDb1.removeByQuery("",
                 new SearchSpec.Builder()
                         .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
-                        .addSchema(AppSearchEmail.SCHEMA_TYPE)
+                        .addSchemaType(AppSearchEmail.SCHEMA_TYPE)
                         .build()));
 
         // Make sure it's really gone
@@ -902,7 +902,7 @@
         checkIsResultSuccess(mDb1.removeByQuery("",
                 new SearchSpec.Builder()
                         .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
-                        .addSchema(AppSearchEmail.SCHEMA_TYPE)
+                        .addSchemaType(AppSearchEmail.SCHEMA_TYPE)
                         .build()));
 
         // Make sure it's really gone in instance 1
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GlobalSearchSessionTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GlobalSearchSessionTest.java
index 2a60798..b06ccfa 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GlobalSearchSessionTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GlobalSearchSessionTest.java
@@ -234,7 +234,7 @@
 
         // Query only for email documents
         searchResults = mGlobalAppSearchManager.query("body", new SearchSpec.Builder()
-                .addSchema(AppSearchEmail.SCHEMA_TYPE)
+                .addSchemaType(AppSearchEmail.SCHEMA_TYPE)
                 .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                 .build());
         documents = convertSearchResultsToDocuments(searchResults);
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSpecTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSpecTest.java
index 3945e06..0d2dae4 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSpecTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSpecTest.java
@@ -28,7 +28,7 @@
     @Test
     public void buildSearchSpecWithoutTermMatchType() {
         assertThrows(RuntimeException.class, () -> new SearchSpec.Builder()
-                .addSchema("testSchemaType")
+                .addSchemaType("testSchemaType")
                 .build());
     }
 
@@ -37,7 +37,7 @@
         SearchSpec searchSpec = new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
                 .addNamespace("namespace1", "namespace2")
-                .addSchema("schemaTypes1", "schemaTypes2")
+                .addSchemaType("schemaTypes1", "schemaTypes2")
                 .setSnippetCount(5)
                 .setSnippetCountPerProperty(10)
                 .setMaxSnippetSize(15)
@@ -49,7 +49,7 @@
         assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
         assertThat(searchSpec.getNamespaces())
                 .containsExactly("namespace1", "namespace2").inOrder();
-        assertThat(searchSpec.getSchemas())
+        assertThat(searchSpec.getSchemaTypes())
                 .containsExactly("schemaTypes1", "schemaTypes2").inOrder();
         assertThat(searchSpec.getSnippetCount()).isEqualTo(5);
         assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10);
@@ -65,7 +65,7 @@
         SearchSpec searchSpec = new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
                 .addNamespace("namespace1", "namespace2")
-                .addSchema("schemaTypes1", "schemaTypes2")
+                .addSchemaType("schemaTypes1", "schemaTypes2")
                 .setSnippetCount(5)
                 .setSnippetCountPerProperty(10)
                 .setMaxSnippetSize(15)
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
index efc3db4..6db51e2 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
@@ -71,7 +71,7 @@
 
     /** Returns the name of this schema type, e.g. Email. */
     @NonNull
-    public String getSchemaTypeName() {
+    public String getSchemaType() {
         return mBundle.getString(SCHEMA_TYPE_FIELD, "");
     }
 
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
index 8645d52..674b906 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
@@ -78,9 +78,8 @@
      * The maximum number of indexed properties a document can have.
      *
      * <p>Indexed properties are properties where the
-     * {@link androidx.appsearch.annotation.AppSearchDocument.Property#indexingType} constant is
-     * anything other than {@link
-     * androidx.appsearch.app.AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
+     * {@link AppSearchSchema.PropertyConfig#getIndexingType()} constant is anything other than
+     * {@link AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
      */
     public static int getMaxIndexedProperties() {
         return MAX_INDEXED_PROPERTIES;
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
index b8c5199..aa1493b 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
@@ -153,12 +153,12 @@
      * <p>If empty, the query will search over all schema types.
      */
     @NonNull
-    public List<String> getSchemas() {
-        List<String> schemas = mBundle.getStringArrayList(SCHEMA_TYPE_FIELD);
-        if (schemas == null) {
+    public List<String> getSchemaTypes() {
+        List<String> schemaTypes = mBundle.getStringArrayList(SCHEMA_TYPE_FIELD);
+        if (schemaTypes == null) {
             return Collections.emptyList();
         }
-        return Collections.unmodifiableList(schemas);
+        return Collections.unmodifiableList(schemaTypes);
     }
 
     /**
@@ -240,10 +240,10 @@
          * <p>If unset, the query will search over all schema types.
          */
         @NonNull
-        public Builder addSchema(@NonNull String... schemaTypes) {
+        public Builder addSchemaType(@NonNull String... schemaTypes) {
             Preconditions.checkNotNull(schemaTypes);
             Preconditions.checkState(!mBuilt, "Builder has already been used");
-            return addSchema(Arrays.asList(schemaTypes));
+            return addSchemaType(Arrays.asList(schemaTypes));
         }
 
         /**
@@ -253,7 +253,7 @@
          * <p>If unset, the query will search over all schema types.
          */
         @NonNull
-        public Builder addSchema(@NonNull Collection<String> schemaTypes) {
+        public Builder addSchemaType(@NonNull Collection<String> schemaTypes) {
             Preconditions.checkNotNull(schemaTypes);
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             mSchemaTypes.addAll(schemaTypes);
@@ -269,7 +269,7 @@
          * @param dataClasses classes annotated with
          *                    {@link androidx.appsearch.annotation.AppSearchDocument}.
          */
-        @SuppressLint("MissingGetterMatchingBuilder")  // Merged list available from getSchemas()
+        @SuppressLint("MissingGetterMatchingBuilder")  // Merged list available from getSchemaTypes
         @NonNull
         public Builder addSchemaByDataClass(@NonNull Collection<Class<?>> dataClasses)
                 throws AppSearchException {
@@ -281,7 +281,7 @@
                 DataClassFactory<?> factory = registry.getOrCreateFactory(dataClass);
                 schemaTypes.add(factory.getSchemaType());
             }
-            addSchema(schemaTypes);
+            addSchemaType(schemaTypes);
             return this;
         }
 
diff --git a/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java b/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
index 82be96d..ae2427f 100644
--- a/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
+++ b/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
@@ -396,7 +396,7 @@
     @Test
     public void testRemoveEmptyDatabase_NoExceptionThrown() throws Exception {
         SearchSpec searchSpec =
-                new SearchSpec.Builder().addSchema("FakeType").setTermMatch(
+                new SearchSpec.Builder().addSchemaType("FakeType").setTermMatch(
                         TermMatchType.Code.PREFIX_VALUE).build();
         mAppSearchImpl.removeByQuery("EmptyDatabase",
                 "", searchSpec);
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
index 6a201f5..23583b0 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
@@ -89,7 +89,7 @@
                 Set<String> schemasHiddenFromPlatformSurfaces =
                         new ArraySet<>(appSearchSchemasHiddenFromPlatformSurfaces.size());
                 for (AppSearchSchema schema : appSearchSchemasHiddenFromPlatformSurfaces) {
-                    schemasHiddenFromPlatformSurfaces.add(schema.getSchemaTypeName());
+                    schemasHiddenFromPlatformSurfaces.add(schema.getSchemaType());
                 }
 
                 mAppSearchImpl.setVisibility(mDatabaseName, schemasHiddenFromPlatformSurfaces);
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java
index 296cc59..86855e1 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java
@@ -44,7 +44,7 @@
     public static SchemaTypeConfigProto convert(@NonNull AppSearchSchema schema) {
         Preconditions.checkNotNull(schema);
         SchemaTypeConfigProto.Builder protoBuilder =
-                SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaTypeName());
+                SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaType());
         List<AppSearchSchema.PropertyConfig> properties = schema.getProperties();
         for (int i = 0; i < properties.size(); i++) {
             PropertyConfigProto propertyProto = convertProperty(properties.get(i));
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
index 810b69d..0e1d933 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
@@ -39,7 +39,7 @@
     public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) {
         Preconditions.checkNotNull(spec);
         SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder()
-                .addAllSchemaTypeFilters(spec.getSchemas())
+                .addAllSchemaTypeFilters(spec.getSchemaTypes())
                 .addAllNamespaceFilters(spec.getNamespaces());
 
         @SearchSpec.TermMatch int termMatchCode = spec.getTermMatch();
diff --git a/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/test/ActionsTest.kt b/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/test/ActionsTest.kt
index f58c8ca..cee0b1f 100644
--- a/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/test/ActionsTest.kt
+++ b/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/test/ActionsTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.benchmark.macro.test
 
-import android.content.Intent
 import androidx.benchmark.macro.CompilationMode
 import androidx.benchmark.macro.MacrobenchmarkScope
 import androidx.benchmark.macro.compile
@@ -40,9 +39,7 @@
     fun killTest() {
         val scope = MacrobenchmarkScope(PACKAGE_NAME)
         scope.pressHome()
-        scope.launchPackageAndWait { intent ->
-            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
-        }
+        scope.launchPackageAndWait()
         assertTrue(isProcessAlive(PACKAGE_NAME))
         scope.killProcess()
         assertFalse(isProcessAlive(PACKAGE_NAME))
@@ -58,9 +55,7 @@
         compilation.compile(PACKAGE_NAME) {
             executions += 1
             scope.pressHome()
-            scope.launchPackageAndWait { intent ->
-                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
-            }
+            scope.launchPackageAndWait()
         }
         assertEquals(iterations, executions)
     }
diff --git a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index 12feb34..9ca2ce7 100644
--- a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -33,7 +33,11 @@
     private val context = instrumentation.context
     private val device = UiDevice.getInstance(instrumentation)
 
-    fun launchPackageAndWait(block: (Intent) -> Unit) {
+    fun launchPackageAndWait(
+        block: (Intent) -> Unit = {
+            it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+        }
+    ) {
         val intent = context.packageManager.getLaunchIntentForPackage(packageName)!!
         block(intent)
         context.startActivity(intent)
diff --git a/benchmark/benchmark-simple-macro-benchmark/src/androidTest/java/androidx/benchmark/macro/sample/MacroBenchmarkTest.kt b/benchmark/benchmark-simple-macro-benchmark/src/androidTest/java/androidx/benchmark/macro/sample/MacroBenchmarkTest.kt
index a7359f6..b1d3aae 100644
--- a/benchmark/benchmark-simple-macro-benchmark/src/androidTest/java/androidx/benchmark/macro/sample/MacroBenchmarkTest.kt
+++ b/benchmark/benchmark-simple-macro-benchmark/src/androidTest/java/androidx/benchmark/macro/sample/MacroBenchmarkTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.benchmark.macro.sample
 
-import android.content.Intent
 import androidx.benchmark.macro.CompilationMode
 import androidx.benchmark.macro.CpuUsageMetric
 import androidx.benchmark.macro.MacrobenchmarkConfig
@@ -47,9 +46,6 @@
         )
     ) {
         pressHome()
-        launchPackageAndWait { launchIntent ->
-            // Clear out any previous instances
-            launchIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
-        }
+        launchPackageAndWait()
     }
 }
diff --git a/biometric/biometric/api/current.txt b/biometric/biometric/api/current.txt
index 2d2401f..5870bdd 100644
--- a/biometric/biometric/api/current.txt
+++ b/biometric/biometric/api/current.txt
@@ -94,3 +94,107 @@
 
 }
 
+package androidx.biometric.auth {
+
+  public interface AuthPrompt {
+    method public void cancelAuthentication();
+  }
+
+  public abstract class AuthPromptCallback {
+    ctor public AuthPromptCallback();
+    method public void onAuthenticationError(androidx.fragment.app.FragmentActivity?, int, CharSequence);
+    method public void onAuthenticationFailed(androidx.fragment.app.FragmentActivity?);
+    method public void onAuthenticationSucceeded(androidx.fragment.app.FragmentActivity?, androidx.biometric.BiometricPrompt.AuthenticationResult);
+  }
+
+  public class AuthPromptHost {
+    ctor public AuthPromptHost(androidx.fragment.app.Fragment);
+    ctor public AuthPromptHost(androidx.fragment.app.FragmentActivity);
+    method public androidx.fragment.app.FragmentActivity? getActivity();
+    method public androidx.fragment.app.Fragment? getFragment();
+  }
+
+  public class Class2BiometricAuthPrompt {
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class2BiometricAuthPrompt.Builder {
+    ctor public Class2BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class2BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt build();
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class Class2BiometricOrCredentialAuthPrompt {
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class2BiometricOrCredentialAuthPrompt.Builder {
+    ctor public Class2BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class2BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt build();
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class Class3BiometricAuthPrompt {
+    method public androidx.biometric.BiometricPrompt.CryptoObject? getCrypto();
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class3BiometricAuthPrompt.Builder {
+    ctor public Class3BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class3BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt build();
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setCrypto(androidx.biometric.BiometricPrompt.CryptoObject);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class Class3BiometricOrCredentialAuthPrompt {
+    method public androidx.biometric.BiometricPrompt.CryptoObject? getCrypto();
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class3BiometricOrCredentialAuthPrompt.Builder {
+    ctor public Class3BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class3BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt build();
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setCrypto(androidx.biometric.BiometricPrompt.CryptoObject);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class CredentialAuthPrompt {
+    method public androidx.biometric.BiometricPrompt.CryptoObject? getCrypto();
+    method public CharSequence? getDescription();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class CredentialAuthPrompt.Builder {
+    ctor public CredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public CredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.CredentialAuthPrompt build();
+    method public androidx.biometric.auth.CredentialAuthPrompt.Builder setCrypto(androidx.biometric.BiometricPrompt.CryptoObject);
+    method public androidx.biometric.auth.CredentialAuthPrompt.Builder setDescription(CharSequence);
+  }
+
+}
+
diff --git a/biometric/biometric/api/public_plus_experimental_current.txt b/biometric/biometric/api/public_plus_experimental_current.txt
index 2d2401f..5870bdd 100644
--- a/biometric/biometric/api/public_plus_experimental_current.txt
+++ b/biometric/biometric/api/public_plus_experimental_current.txt
@@ -94,3 +94,107 @@
 
 }
 
+package androidx.biometric.auth {
+
+  public interface AuthPrompt {
+    method public void cancelAuthentication();
+  }
+
+  public abstract class AuthPromptCallback {
+    ctor public AuthPromptCallback();
+    method public void onAuthenticationError(androidx.fragment.app.FragmentActivity?, int, CharSequence);
+    method public void onAuthenticationFailed(androidx.fragment.app.FragmentActivity?);
+    method public void onAuthenticationSucceeded(androidx.fragment.app.FragmentActivity?, androidx.biometric.BiometricPrompt.AuthenticationResult);
+  }
+
+  public class AuthPromptHost {
+    ctor public AuthPromptHost(androidx.fragment.app.Fragment);
+    ctor public AuthPromptHost(androidx.fragment.app.FragmentActivity);
+    method public androidx.fragment.app.FragmentActivity? getActivity();
+    method public androidx.fragment.app.Fragment? getFragment();
+  }
+
+  public class Class2BiometricAuthPrompt {
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class2BiometricAuthPrompt.Builder {
+    ctor public Class2BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class2BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt build();
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class Class2BiometricOrCredentialAuthPrompt {
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class2BiometricOrCredentialAuthPrompt.Builder {
+    ctor public Class2BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class2BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt build();
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class Class3BiometricAuthPrompt {
+    method public androidx.biometric.BiometricPrompt.CryptoObject? getCrypto();
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class3BiometricAuthPrompt.Builder {
+    ctor public Class3BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class3BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt build();
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setCrypto(androidx.biometric.BiometricPrompt.CryptoObject);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class Class3BiometricOrCredentialAuthPrompt {
+    method public androidx.biometric.BiometricPrompt.CryptoObject? getCrypto();
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class3BiometricOrCredentialAuthPrompt.Builder {
+    ctor public Class3BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class3BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt build();
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setCrypto(androidx.biometric.BiometricPrompt.CryptoObject);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class CredentialAuthPrompt {
+    method public androidx.biometric.BiometricPrompt.CryptoObject? getCrypto();
+    method public CharSequence? getDescription();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class CredentialAuthPrompt.Builder {
+    ctor public CredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public CredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.CredentialAuthPrompt build();
+    method public androidx.biometric.auth.CredentialAuthPrompt.Builder setCrypto(androidx.biometric.BiometricPrompt.CryptoObject);
+    method public androidx.biometric.auth.CredentialAuthPrompt.Builder setDescription(CharSequence);
+  }
+
+}
+
diff --git a/biometric/biometric/api/restricted_current.txt b/biometric/biometric/api/restricted_current.txt
index 2d2401f..5870bdd 100644
--- a/biometric/biometric/api/restricted_current.txt
+++ b/biometric/biometric/api/restricted_current.txt
@@ -94,3 +94,107 @@
 
 }
 
+package androidx.biometric.auth {
+
+  public interface AuthPrompt {
+    method public void cancelAuthentication();
+  }
+
+  public abstract class AuthPromptCallback {
+    ctor public AuthPromptCallback();
+    method public void onAuthenticationError(androidx.fragment.app.FragmentActivity?, int, CharSequence);
+    method public void onAuthenticationFailed(androidx.fragment.app.FragmentActivity?);
+    method public void onAuthenticationSucceeded(androidx.fragment.app.FragmentActivity?, androidx.biometric.BiometricPrompt.AuthenticationResult);
+  }
+
+  public class AuthPromptHost {
+    ctor public AuthPromptHost(androidx.fragment.app.Fragment);
+    ctor public AuthPromptHost(androidx.fragment.app.FragmentActivity);
+    method public androidx.fragment.app.FragmentActivity? getActivity();
+    method public androidx.fragment.app.Fragment? getFragment();
+  }
+
+  public class Class2BiometricAuthPrompt {
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class2BiometricAuthPrompt.Builder {
+    ctor public Class2BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class2BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt build();
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class2BiometricAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class Class2BiometricOrCredentialAuthPrompt {
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class2BiometricOrCredentialAuthPrompt.Builder {
+    ctor public Class2BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class2BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt build();
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class Class3BiometricAuthPrompt {
+    method public androidx.biometric.BiometricPrompt.CryptoObject? getCrypto();
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class3BiometricAuthPrompt.Builder {
+    ctor public Class3BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class3BiometricAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt build();
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setCrypto(androidx.biometric.BiometricPrompt.CryptoObject);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class3BiometricAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class Class3BiometricOrCredentialAuthPrompt {
+    method public androidx.biometric.BiometricPrompt.CryptoObject? getCrypto();
+    method public CharSequence? getDescription();
+    method public CharSequence? getSubtitle();
+    method public boolean isConfirmationRequired();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class Class3BiometricOrCredentialAuthPrompt.Builder {
+    ctor public Class3BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public Class3BiometricOrCredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt build();
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setCrypto(androidx.biometric.BiometricPrompt.CryptoObject);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setDescription(CharSequence);
+    method public androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt.Builder setSubtitle(CharSequence);
+  }
+
+  public class CredentialAuthPrompt {
+    method public androidx.biometric.BiometricPrompt.CryptoObject? getCrypto();
+    method public CharSequence? getDescription();
+    method public androidx.biometric.auth.AuthPrompt startAuthentication();
+  }
+
+  public static final class CredentialAuthPrompt.Builder {
+    ctor public CredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, java.util.concurrent.Executor, androidx.biometric.auth.AuthPromptCallback);
+    ctor public CredentialAuthPrompt.Builder(androidx.biometric.auth.AuthPromptHost, CharSequence, androidx.biometric.auth.AuthPromptCallback);
+    method public androidx.biometric.auth.CredentialAuthPrompt build();
+    method public androidx.biometric.auth.CredentialAuthPrompt.Builder setCrypto(androidx.biometric.BiometricPrompt.CryptoObject);
+    method public androidx.biometric.auth.CredentialAuthPrompt.Builder setDescription(CharSequence);
+  }
+
+}
+
diff --git a/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java b/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java
index 5383e59..3b28c82 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java
@@ -263,9 +263,8 @@
         if (activity == null) {
             return;
         }
-
         mViewModel = new ViewModelProvider(getActivity()).get(BiometricViewModel.class);
-
+        mViewModel.setClientActivity(activity);
         mViewModel.getAuthenticationResult().observe(
                 this,
                 new Observer<BiometricPrompt.AuthenticationResult>() {
diff --git a/biometric/biometric/src/main/java/androidx/biometric/BiometricPrompt.java b/biometric/biometric/src/main/java/androidx/biometric/BiometricPrompt.java
index fea9966..8b7d3cb 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/BiometricPrompt.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/BiometricPrompt.java
@@ -25,6 +25,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
 import androidx.biometric.BiometricManager.Authenticators;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
@@ -155,6 +156,7 @@
 
     /**
      * An error code that may be returned during authentication.
+     * @hide
      */
     @IntDef({
         ERROR_HW_UNAVAILABLE,
@@ -171,8 +173,9 @@
         ERROR_NEGATIVE_BUTTON,
         ERROR_NO_DEVICE_CREDENTIAL
     })
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
     @Retention(RetentionPolicy.SOURCE)
-    @interface AuthenticationError {}
+    public @interface AuthenticationError {}
 
     /**
      * Authentication type reported by {@link AuthenticationResult} when the user authenticated via
diff --git a/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java b/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java
index 1d63e18..12ecf70 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java
@@ -23,6 +23,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.fragment.app.FragmentActivity;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.ViewModel;
@@ -151,6 +152,11 @@
     @Nullable private BiometricPrompt.AuthenticationCallback mClientCallback;
 
     /**
+     * Reference to latest {@link androidx.fragment.app.FragmentActivity} hosting BiometricPrompt
+     */
+    @NonNull private WeakReference<FragmentActivity> mClientActivity;
+
+    /**
      * Info about the appearance and behavior of the prompt provided by the client application.
      */
     @Nullable private BiometricPrompt.PromptInfo mPromptInfo;
@@ -286,6 +292,24 @@
         mClientCallback = clientCallback;
     }
 
+    /**
+     * Returns reference to latest activity hosting BiometricPrompt or null if activity has
+     * already been destroyed
+     * @return Reference to latest activity hosting BiometricPrompt
+     */
+    @Nullable
+    public FragmentActivity getClientActivity() {
+        return mClientActivity.get();
+    }
+
+    /**
+     * Updates reference to latest activity hosting BiometricPrompt
+     * @param clientActivity Reference to latest activity hosting BiometricPrompt
+     */
+    void setClientActivity(@NonNull FragmentActivity clientActivity) {
+        mClientActivity = new WeakReference<>(clientActivity);
+    }
+
     void setPromptInfo(@Nullable BiometricPrompt.PromptInfo promptInfo) {
         mPromptInfo = promptInfo;
     }
diff --git a/biometric/biometric/src/main/java/androidx/biometric/auth/AuthPrompt.java b/biometric/biometric/src/main/java/androidx/biometric/auth/AuthPrompt.java
new file mode 100644
index 0000000..4b64ac4
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/auth/AuthPrompt.java
@@ -0,0 +1,30 @@
+/*
+ * 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.biometric.auth;
+
+/**
+ * A wrapper class for {@link Class2BiometricAuthPrompt}, {@link Class3BiometricAuthPrompt}
+ * {@link Class2BiometricOrCredentialAuthPrompt}, {@link Class3BiometricOrCredentialAuthPrompt}
+ * and {@link CredentialAuthPrompt} that allows for cancellation and dismissal of the current
+ * authentication prompt.
+ */
+public interface AuthPrompt {
+    /**
+     * Cancels authentication of the current BiometricPrompt.
+     */
+    void cancelAuthentication();
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/auth/AuthPromptCallback.java b/biometric/biometric/src/main/java/androidx/biometric/auth/AuthPromptCallback.java
new file mode 100644
index 0000000..68b4a80
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/auth/AuthPromptCallback.java
@@ -0,0 +1,65 @@
+/*
+ * 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.biometric.auth;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.biometric.BiometricPrompt;
+import androidx.fragment.app.FragmentActivity;
+
+/**
+ * A collection of methods that may be invoked by {@link Class2BiometricAuthPrompt},
+ * {@link Class3BiometricAuthPrompt}, {@link Class2BiometricOrCredentialAuthPrompt},
+ * {@link Class3BiometricOrCredentialAuthPrompt}, or {@link CredentialAuthPrompt} during
+ * authentication, returning the {@link androidx.fragment.app.FragmentActivity} the
+ * prompt is attached to.
+ */
+public abstract class AuthPromptCallback {
+    /**
+     * Called when an unrecoverable error has been encountered and authentication has stopped.
+     *
+     * <p>After this method is called, no further events will be sent for the current
+     * authentication session.
+     *
+     * @param activity {@link androidx.fragment.app.FragmentActivity} the prompt is attached to
+     * @param errorCode An integer ID associated with the error.
+     * @param errString A human-readable string that describes the error.
+     */
+    public void onAuthenticationError(@Nullable FragmentActivity activity,
+            @BiometricPrompt.AuthenticationError int errorCode, @NonNull CharSequence errString) {}
+
+    /**
+     * Called when a biometric (e.g. fingerprint, face, etc.) is recognized, indicating that the
+     * user has successfully authenticated.
+     *
+     * <p>After this method is called, no further events will be sent for the current
+     * authentication session.
+     *
+     * @param activity {@link androidx.fragment.app.FragmentActivity} the prompt is attached to
+     * @param result An object containing authentication-related data.
+     */
+    public void onAuthenticationSucceeded(@Nullable FragmentActivity activity,
+            @NonNull BiometricPrompt.AuthenticationResult result) {}
+
+    /**
+     * Called when a biometric (e.g. fingerprint, face, etc.) is presented but not recognized as
+     * belonging to the user.
+     *
+     * @param activity {@link androidx.fragment.app.FragmentActivity} the prompt is attached to
+     */
+    public void onAuthenticationFailed(@Nullable FragmentActivity activity) {}
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/auth/AuthPromptHost.java b/biometric/biometric/src/main/java/androidx/biometric/auth/AuthPromptHost.java
new file mode 100644
index 0000000..7d9eb49
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/auth/AuthPromptHost.java
@@ -0,0 +1,49 @@
+/*
+ * 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.biometric.auth;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+/**
+ * A wrapper class for {@link FragmentActivity} and {@link Fragment} with the FragmentActivity or
+ * Fragment that hosts the AuthPrompt
+ */
+public class AuthPromptHost {
+    @Nullable private Fragment mFragment = null;
+    @Nullable private FragmentActivity mActivity = null;
+
+    public AuthPromptHost(@NonNull Fragment fragment) {
+        mFragment = fragment;
+    }
+
+    public AuthPromptHost(@NonNull FragmentActivity activity) {
+        mActivity = activity;
+    }
+
+    @Nullable
+    public Fragment getFragment() {
+        return mFragment;
+    }
+
+    @Nullable
+    public FragmentActivity getActivity() {
+        return mActivity;
+    }
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/auth/Class2BiometricAuthPrompt.java b/biometric/biometric/src/main/java/androidx/biometric/auth/Class2BiometricAuthPrompt.java
new file mode 100644
index 0000000..75127bf
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/auth/Class2BiometricAuthPrompt.java
@@ -0,0 +1,319 @@
+/*
+ * 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.biometric.auth;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.biometric.BiometricManager;
+import androidx.biometric.BiometricPrompt;
+import androidx.biometric.BiometricViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This class is used to build and configure a {@link BiometricPrompt} for authentication that
+ * only permits Class 2 biometric modalities (fingerprint, iris, face, etc), and then start
+ * authentication.
+ *
+ * Class 2 (formerly known as Weak) refers to the strength of the biometric sensor, as specified
+ * in the Android 11 CDD. Class 2 authentication can be used for applications that don't require
+ * cryptographic operations.
+ */
+public class Class2BiometricAuthPrompt {
+
+    /**
+     * The default executor provided when not provided in the {@link Class2BiometricAuthPrompt}
+     * constructor.
+     */
+    private static class DefaultExecutor implements Executor {
+        private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        DefaultExecutor() {}
+
+        @Override
+        public void execute(Runnable runnable) {
+            mHandler.post(runnable);
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    @NonNull BiometricPrompt mBiometricPrompt;
+    @NonNull private BiometricPrompt.PromptInfo mPromptInfo;
+    private boolean mIsConfirmationRequired;
+
+    @Nullable private CharSequence mSubtitle;
+    @Nullable private CharSequence mDescription;
+
+    /**
+     * Constructs a {@link Class2BiometricAuthPrompt}, which can be used to begin authentication.
+     * @param biometricPrompt Manages a system-provided biometric prompt for authentication
+     * @param promptInfo A set of configurable options for how the {@link BiometricPrompt}
+     *                   should appear and behave.
+     * @param subtitle The subtitle to be displayed on the prompt.
+     * @param description The description to be displayed on the prompt.
+     * @param confirmationRequired Whether explicit user confirmation is required after a
+     *                             passive biometric
+     */
+    Class2BiometricAuthPrompt(
+            @NonNull BiometricPrompt biometricPrompt,
+            @NonNull BiometricPrompt.PromptInfo promptInfo,
+            @NonNull CharSequence subtitle,
+            @NonNull CharSequence description,
+            boolean confirmationRequired) {
+        mBiometricPrompt = biometricPrompt;
+        mPromptInfo = promptInfo;
+        mSubtitle = subtitle;
+        mDescription = description;
+        mIsConfirmationRequired = confirmationRequired;
+    }
+
+    /**
+     * Begins authentication using the configured biometric prompt, and returns an
+     * {@link AuthPrompt} wrapper that can be used for cancellation and dismissal of the
+     * biometric prompt.
+     * @return {@link AuthPrompt} wrapper that can be used for cancellation and dismissal of the
+     * biometric prompt using {@link AuthPrompt#cancelAuthentication()}
+     */
+    @NonNull
+    public AuthPrompt startAuthentication() {
+        mBiometricPrompt.authenticate(mPromptInfo);
+        return new AuthPrompt() {
+            @Override
+            public void cancelAuthentication() {
+                mBiometricPrompt.cancelAuthentication();
+            }
+        };
+    }
+
+    /**
+     * Gets the subtitle for the prompt.
+     * @return subtitle for the prompt
+     */
+    @Nullable
+    public CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * Gets the description for the prompt. Defaults to null.
+     * @return description for the prompt
+     */
+    @Nullable
+    public CharSequence getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Indicates whether prompt requires explicit user confirmation after a passive biometric (e
+     * .g. iris or face) has been recognized but before
+     * {@link AuthPromptCallback#onAuthenticationSucceeded(androidx.fragment.app.FragmentActivity,
+     * BiometricPrompt.AuthenticationResult)} is called.
+     * @return whether prompt requires explicit user confirmation after a passive biometric.
+     */
+    public boolean isConfirmationRequired() {
+        return mIsConfirmationRequired;
+    }
+
+    /**
+     * Builds a {@link BiometricPrompt} object for Class 2 biometric only authentication with
+     * specified options.
+     */
+    public static final class Builder {
+
+        // Nullable options on the builder
+        @Nullable private CharSequence mSubtitle = null;
+        @Nullable private CharSequence mDescription = null;
+
+        // Non-null options on the builder.
+        @NonNull private final AuthPromptHost mAuthPromptHost;
+        @NonNull private final CharSequence mTitle;
+        @NonNull private final CharSequence mNegativeButtonText;
+        @NonNull private final Executor mClientExecutor;
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        @NonNull final AuthPromptCallback mClientCallback;
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        @NonNull final BiometricViewModel mViewModel;
+
+        private boolean mIsConfirmationRequired = true;
+
+        /**
+         * A builder used to set individual options for the {@link Class2BiometricAuthPrompt}
+         * class to construct a {@link BiometricPrompt} for class 2 biometric only authentication.
+         *
+         * @param authPromptHost Contains {@link androidx.fragment.app.Fragment} or
+         * {@link androidx.fragment.app.FragmentActivity} to host the authentication prompt
+         * @param title The title to be displayed on the prompt.
+         * @param negativeButtonText The label to be used for the negative button on the prompt.
+         * @param clientExecutor The executor that will run authentication callback methods.
+         * @param clientCallback The object that will receive and process authentication events.
+         */
+        public Builder(
+                @NonNull AuthPromptHost authPromptHost,
+                @NonNull CharSequence title,
+                @NonNull CharSequence negativeButtonText,
+                @NonNull Executor clientExecutor,
+                @NonNull AuthPromptCallback clientCallback) {
+            mAuthPromptHost = authPromptHost;
+            mTitle = title;
+            mNegativeButtonText = negativeButtonText;
+            mClientExecutor = clientExecutor;
+            mClientCallback = clientCallback;
+            mViewModel = new ViewModelProvider(mAuthPromptHost.getActivity())
+                    .get(BiometricViewModel.class);
+        }
+
+        /**
+         * A builder used to set individual options for the {@link Class2BiometricAuthPrompt}
+         * class to construct a {@link BiometricPrompt} for class 2 biometric only authentication.
+         * Sets mClientExecutor to new DefaultExecutor() object.
+         *
+         * @param authPromptHost Contains {@link androidx.fragment.app.Fragment} or
+         * {@link androidx.fragment.app.FragmentActivity} to host the authentication prompt
+         * @param title The title to be displayed on the prompt.
+         * @param negativeButtonText The label to be used for the negative button on the prompt.
+         * @param clientCallback The object that will receive and process authentication events.
+         */
+        public Builder(
+                @NonNull AuthPromptHost authPromptHost,
+                @NonNull CharSequence title,
+                @NonNull CharSequence negativeButtonText,
+                @NonNull AuthPromptCallback clientCallback) {
+            mAuthPromptHost = authPromptHost;
+            mTitle = title;
+            mNegativeButtonText = negativeButtonText;
+            mClientExecutor = new DefaultExecutor();
+            mClientCallback = clientCallback;
+            mViewModel = new ViewModelProvider(mAuthPromptHost.getActivity())
+                    .get(BiometricViewModel.class);
+        }
+
+        /**
+         * Optional: Sets the subtitle for the prompt. Defaults to null.
+         *
+         * @param subtitle The subtitle to be displayed on the prompt.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setSubtitle(@NonNull CharSequence subtitle) {
+            mSubtitle = subtitle;
+            return this;
+        }
+
+        /**
+         * Optional: Sets the description for the prompt. Defaults to null.
+         *
+         * @param description The description to be displayed on the prompt.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setDescription(@NonNull CharSequence description) {
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Optional: Sets a system hint for whether to require explicit user confirmation after a
+         * passive biometric (e.g. iris or face) has been recognized but before {@link
+         * AuthPromptCallback#onAuthenticationSucceeded(androidx.fragment.app.FragmentActivity,
+         * BiometricPrompt.AuthenticationResult)} is called. Defaults to {@code true}.
+         *
+         * <p>Disabling this option is generally only appropriate for frequent, low-value
+         * transactions, such as re-authenticating for a previously authorized application.
+         *
+         * <p>Also note that, as it is merely a hint, this option may be ignored by the system.
+         * For example, the system may choose to instead always require confirmation if the user
+         * has disabled passive authentication for their device in Settings. Additionally, this
+         * option will be ignored on devices running OS versions prior to Android 10 (API 29).
+         *
+         * @param confirmationRequired Whether this option should be enabled.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setConfirmationRequired(boolean confirmationRequired) {
+            mIsConfirmationRequired = confirmationRequired;
+            return this;
+        }
+
+        /**
+         * Configures a {@link BiometricPrompt} object with the specified options, and returns a
+         * {@link Class2BiometricAuthPrompt} instance that can be used for starting authentication.
+         * @return {@link Class2BiometricAuthPrompt} instance for starting authentication.
+         */
+        @NonNull
+        public Class2BiometricAuthPrompt build() {
+            final BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo
+                    .Builder()
+                    .setTitle(mTitle)
+                    .setSubtitle(mSubtitle)
+                    .setDescription(mDescription)
+                    .setNegativeButtonText(mNegativeButtonText)
+                    .setConfirmationRequired(mIsConfirmationRequired)
+                    .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_WEAK)
+                    .build();
+            final BiometricPrompt biometricPrompt;
+            BiometricPrompt.AuthenticationCallback wrappedCallback =
+                    new BiometricPrompt.AuthenticationCallback() {
+                        @Override
+                        public void onAuthenticationError(int errorCode,
+                                @NonNull CharSequence errString) {
+                            mClientCallback.onAuthenticationError(
+                                    mViewModel.getClientActivity(),
+                                    errorCode,
+                                    errString
+                            );
+                        }
+
+                        @Override
+                        public void onAuthenticationSucceeded(
+                                @NonNull BiometricPrompt.AuthenticationResult result) {
+                            mClientCallback.onAuthenticationSucceeded(
+                                    mViewModel.getClientActivity(),
+                                    result
+                            );
+                        }
+
+                        @Override
+                        public void onAuthenticationFailed() {
+                            mClientCallback.onAuthenticationFailed(
+                                    mViewModel.getClientActivity()
+                            );
+                        }
+                    };
+            if (mAuthPromptHost.getActivity() != null) {
+                biometricPrompt = new BiometricPrompt(mAuthPromptHost.getActivity(),
+                        mClientExecutor, wrappedCallback);
+            } else if (mAuthPromptHost.getFragment() != null) {
+                biometricPrompt = new BiometricPrompt(mAuthPromptHost.getFragment(),
+                        mClientExecutor, wrappedCallback);
+            } else {
+                throw new IllegalArgumentException("Invalid AuthPromptHost provided. Must "
+                        + "provide AuthPromptHost containing Fragment or FragmentActivity for"
+                        + " hosting the BiometricPrompt.");
+            }
+
+            return new Class2BiometricAuthPrompt(biometricPrompt, promptInfo, mSubtitle,
+                    mDescription, mIsConfirmationRequired);
+        }
+    }
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/auth/Class2BiometricOrCredentialAuthPrompt.java b/biometric/biometric/src/main/java/androidx/biometric/auth/Class2BiometricOrCredentialAuthPrompt.java
new file mode 100644
index 0000000..a099eca
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/auth/Class2BiometricOrCredentialAuthPrompt.java
@@ -0,0 +1,316 @@
+/*
+ * 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.biometric.auth;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.biometric.BiometricManager;
+import androidx.biometric.BiometricPrompt;
+import androidx.biometric.BiometricViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This class is used to build and configure a {@link BiometricPrompt} for authentication that
+ * permits Class 2 biometric modalities (fingerprint, iris, face, etc), or device credential
+ * modalities (device PIN, pattern, or password), and then start authentication.
+ *
+ * Class 2 (formerly known as Weak) refers to the strength of the biometric sensor, as specified
+ * in the Android 11 CDD. Class 2 authentication can be used for applications that don't require
+ * cryptographic operations.
+ */
+public class Class2BiometricOrCredentialAuthPrompt {
+
+    /**
+     * The default executor provided when not provided in the
+     * {@link Class2BiometricOrCredentialAuthPrompt} constructor.
+     */
+    private static class DefaultExecutor implements Executor {
+        private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        DefaultExecutor() {}
+
+        @Override
+        public void execute(Runnable runnable) {
+            mHandler.post(runnable);
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    @NonNull BiometricPrompt mBiometricPrompt;
+    @NonNull private BiometricPrompt.PromptInfo mPromptInfo;
+    private boolean mIsConfirmationRequired;
+
+    @Nullable private CharSequence mSubtitle;
+    @Nullable private CharSequence mDescription;
+
+    /**
+     * Constructs a {@link Class2BiometricOrCredentialAuthPrompt}, which can be used to begin
+     * authentication.
+     * @param biometricPrompt Manages a system-provided biometric prompt for authentication
+     * @param promptInfo A set of configurable options for how the {@link BiometricPrompt}
+     *                   should appear and behave.
+     * @param subtitle The subtitle to be displayed on the prompt.
+     * @param description The description to be displayed on the prompt.
+     * @param confirmationRequired Whether explicit user confirmation is required after a
+     *                             passive biometric
+     */
+    Class2BiometricOrCredentialAuthPrompt(
+            @NonNull BiometricPrompt biometricPrompt,
+            @NonNull BiometricPrompt.PromptInfo promptInfo,
+            @NonNull CharSequence subtitle,
+            @NonNull CharSequence description,
+            boolean confirmationRequired) {
+        mBiometricPrompt = biometricPrompt;
+        mPromptInfo = promptInfo;
+        mSubtitle = subtitle;
+        mDescription = description;
+        mIsConfirmationRequired = confirmationRequired;
+    }
+
+    /**
+     * Begins authentication using the configured biometric prompt, and returns an
+     * {@link AuthPrompt} wrapper that can be used for cancellation and dismissal of the biometric
+     * prompt.
+     * @return {@link AuthPrompt} wrapper that can be used for cancellation and dismissal of the
+     * biometric prompt using {@link AuthPrompt#cancelAuthentication()}
+     */
+    @NonNull
+    public AuthPrompt startAuthentication() {
+        mBiometricPrompt.authenticate(mPromptInfo);
+        return new AuthPrompt() {
+            @Override
+            public void cancelAuthentication() {
+                mBiometricPrompt.cancelAuthentication();
+            }
+        };
+    }
+
+    /**
+     * Gets the subtitle for the prompt.
+     * @return subtitle for the prompt
+     */
+    @Nullable
+    public CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * Gets the description for the prompt. Defaults to null.
+     * @return description for the prompt
+     */
+    @Nullable
+    public CharSequence getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Indicates whether prompt requires explicit user confirmation after a passive biometric (e
+     * .g. iris or face) has been recognized but before
+     * {@link AuthPromptCallback#onAuthenticationSucceeded(androidx.fragment.app.FragmentActivity,
+     * BiometricPrompt.AuthenticationResult)} is called.
+     * @return whether prompt requires explicit user confirmation after a passive biometric.
+     */
+    public boolean isConfirmationRequired() {
+        return mIsConfirmationRequired;
+    }
+
+    /**
+     * Builds a {@link BiometricPrompt} object for class 2 biometric or device credential
+     * authentication with specified options.
+     */
+    public static final class Builder {
+        // Mutable options to be set on the builder.
+        @Nullable private CharSequence mSubtitle = null;
+        @Nullable private CharSequence mDescription = null;
+
+        @NonNull private final AuthPromptHost mAuthPromptHost;
+        @NonNull private final CharSequence mTitle;
+        @NonNull private final Executor mClientExecutor;
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        @NonNull final AuthPromptCallback mClientCallback;
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        @NonNull final BiometricViewModel mViewModel;
+
+        private boolean mIsConfirmationRequired = true;
+
+        /**
+         * A builder used to set individual options for the
+         * {@link Class2BiometricOrCredentialAuthPrompt} class to construct a
+         * {@link BiometricPrompt} for class 2 biometric or device credential authentication.
+         *
+         * @param authPromptHost {@link androidx.fragment.app.Fragment} or
+         * {@link androidx.fragment.app.FragmentActivity} to host the authentication prompt
+         * @param title The title to be displayed on the prompt.
+         * @param clientExecutor The executor that will run authentication callback methods.
+         * @param clientCallback The object that will receive and process authentication events.
+         */
+        public Builder(
+                @NonNull AuthPromptHost authPromptHost,
+                @NonNull CharSequence title,
+                @NonNull Executor clientExecutor,
+                @NonNull AuthPromptCallback clientCallback) {
+            mAuthPromptHost = authPromptHost;
+            mTitle = title;
+            mClientExecutor = clientExecutor;
+            mClientCallback = clientCallback;
+            mViewModel = new ViewModelProvider(mAuthPromptHost.getActivity())
+                    .get(BiometricViewModel.class);
+        }
+
+        /**
+         * A builder used to set individual options for the
+         * {@link Class2BiometricOrCredentialAuthPrompt} class to construct a
+         * {@link BiometricPrompt} for class 2 biometric or device credential authentication.
+         * Sets mClientExecutor to new DefaultExecutor() object.
+         *
+         * @param authPromptHost {@link androidx.fragment.app.Fragment} or
+         * {@link androidx.fragment.app.FragmentActivity} to host the authentication prompt
+         * @param title The title to be displayed on the prompt.
+         * @param clientCallback The object that will receive and process authentication events.
+         */
+        public Builder(
+                @NonNull AuthPromptHost authPromptHost,
+                @NonNull CharSequence title,
+                @NonNull AuthPromptCallback clientCallback) {
+            mAuthPromptHost = authPromptHost;
+            mTitle = title;
+            mClientExecutor = new DefaultExecutor();
+            mClientCallback = clientCallback;
+            mViewModel = new ViewModelProvider(mAuthPromptHost.getActivity())
+                    .get(BiometricViewModel.class);
+        }
+
+        /**
+         * Optional: Sets the subtitle for the prompt. Defaults to null.
+         *
+         * @param subtitle The subtitle to be displayed on the prompt.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setSubtitle(@NonNull CharSequence subtitle) {
+            mSubtitle = subtitle;
+            return this;
+        }
+
+        /**
+         * Optional: Sets the description for the prompt. Defaults to null.
+         *
+         * @param description The description to be displayed on the prompt.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setDescription(@NonNull CharSequence description) {
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Optional: Sets a system hint for whether to require explicit user confirmation after a
+         * passive biometric (e.g. iris or face) has been recognized but before
+         * {@link AuthPromptCallback#onAuthenticationSucceeded(
+         * androidx.fragment.app.FragmentActivity, BiometricPrompt.AuthenticationResult)} is
+         * called. Defaults to {@code true}.
+         *
+         * <p>Disabling this option is generally only appropriate for frequent, low-value
+         * transactions, such as re-authenticating for a previously authorized application.
+         *
+         * <p>Also note that, as it is merely a hint, this option may be ignored by the system.
+         * For example, the system may choose to instead always require confirmation if the user
+         * has disabled passive authentication for their device in Settings. Additionally, this
+         * option will be ignored on devices running OS versions prior to Android 10 (API 29).
+         *
+         * @param confirmationRequired Whether this option should be enabled.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setConfirmationRequired(boolean confirmationRequired) {
+            mIsConfirmationRequired = confirmationRequired;
+            return this;
+        }
+
+        /**
+         * Configures a {@link BiometricPrompt} object with the specified options, and returns a
+         * {@link Class2BiometricOrCredentialAuthPrompt} instance that can be used to start
+         * authentication.
+         * @return {@link Class2BiometricOrCredentialAuthPrompt} instance for starting
+         * authentication.
+         */
+        @NonNull
+        public Class2BiometricOrCredentialAuthPrompt build() {
+            final BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo
+                    .Builder()
+                    .setTitle(mTitle)
+                    .setSubtitle(mSubtitle)
+                    .setDescription(mDescription)
+                    .setConfirmationRequired(mIsConfirmationRequired)
+                    .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_WEAK
+                            | BiometricManager.Authenticators.DEVICE_CREDENTIAL)
+                    .build();
+
+            final BiometricPrompt biometricPrompt;
+            BiometricPrompt.AuthenticationCallback wrappedCallback =
+                    new BiometricPrompt.AuthenticationCallback() {
+                        @Override
+                        public void onAuthenticationError(int errorCode,
+                                @NonNull CharSequence errString) {
+                            mClientCallback.onAuthenticationError(
+                                    mViewModel.getClientActivity(),
+                                    errorCode,
+                                    errString
+                            );
+                        }
+
+                        @Override
+                        public void onAuthenticationSucceeded(
+                                @NonNull BiometricPrompt.AuthenticationResult result) {
+                            mClientCallback.onAuthenticationSucceeded(
+                                    mViewModel.getClientActivity(),
+                                    result
+                            );
+                        }
+
+                        @Override
+                        public void onAuthenticationFailed() {
+                            mClientCallback.onAuthenticationFailed(
+                                    mViewModel.getClientActivity()
+                            );
+                        }
+                    };
+            if (mAuthPromptHost.getActivity() != null) {
+                biometricPrompt = new BiometricPrompt(mAuthPromptHost.getActivity(),
+                        mClientExecutor, wrappedCallback);
+            } else if (mAuthPromptHost.getFragment() != null) {
+                biometricPrompt = new BiometricPrompt(mAuthPromptHost.getFragment(),
+                        mClientExecutor, wrappedCallback);
+            } else {
+                throw new IllegalArgumentException("Invalid AuthPromptHost provided. Must "
+                        + "provide AuthPromptHost containing Fragment or FragmentActivity for"
+                        + " hosting the BiometricPrompt.");
+            }
+            return new Class2BiometricOrCredentialAuthPrompt(biometricPrompt, promptInfo,
+                    mSubtitle, mDescription, mIsConfirmationRequired);
+        }
+    }
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/auth/Class3BiometricAuthPrompt.java b/biometric/biometric/src/main/java/androidx/biometric/auth/Class3BiometricAuthPrompt.java
new file mode 100644
index 0000000..8d84caf
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/auth/Class3BiometricAuthPrompt.java
@@ -0,0 +1,351 @@
+/*
+ * 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.biometric.auth;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.biometric.BiometricManager;
+import androidx.biometric.BiometricPrompt;
+import androidx.biometric.BiometricViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This class is used to build and configure a {@link BiometricPrompt} for authentication that
+ * only permits Class 3 biometric modalities (fingerprint, iris, face, etc), and then start
+ * authentication.
+ *
+ * Class 3 (formerly known as Strong) refers to the strength of the biometric sensor, as specified
+ * in the Android 11 CDD. Class 3 authentication can be used for applications that use
+ * cryptographic operations.
+ */
+public class Class3BiometricAuthPrompt {
+
+    /**
+     * The default executor provided when not provided in the {@link Class3BiometricAuthPrompt}
+     * constructor.
+     */
+    private static class DefaultExecutor implements Executor {
+        private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        DefaultExecutor() {}
+
+        @Override
+        public void execute(Runnable runnable) {
+            mHandler.post(runnable);
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    @NonNull BiometricPrompt mBiometricPrompt;
+    @NonNull private BiometricPrompt.PromptInfo mPromptInfo;
+    private boolean mIsConfirmationRequired;
+
+    @Nullable private BiometricPrompt.CryptoObject mCrypto;
+    @Nullable private CharSequence mSubtitle;
+    @Nullable private CharSequence mDescription;
+
+    /**
+     * Constructs a {@link Class3BiometricAuthPrompt}, which can be used to begin authentication.
+     * @param biometricPrompt Manages a system-provided biometric prompt for authentication
+     * @param promptInfo A set of configurable options for how the {@link BiometricPrompt}
+     *                   should appear and behave.
+     * @param crypto A crypto object to be associated with this authentication.
+     * @param subtitle The subtitle to be displayed on the prompt.
+     * @param description The description to be displayed on the prompt.
+     * @param confirmationRequired Whether explicit user confirmation is required after a
+     *                             passive biometric
+     */
+    Class3BiometricAuthPrompt(
+            @NonNull BiometricPrompt biometricPrompt,
+            @NonNull BiometricPrompt.PromptInfo promptInfo,
+            @Nullable BiometricPrompt.CryptoObject crypto,
+            @NonNull CharSequence subtitle,
+            @NonNull CharSequence description,
+            boolean confirmationRequired) {
+        mBiometricPrompt = biometricPrompt;
+        mPromptInfo = promptInfo;
+        mCrypto = crypto;
+        mSubtitle = subtitle;
+        mDescription = description;
+        mIsConfirmationRequired = confirmationRequired;
+    }
+
+    /**
+     * Begins authentication using the configured biometric prompt, and returns an
+     * {@link AuthPrompt} wrapper that can be used for cancellation and dismissal of the biometric
+     * prompt.
+     * @return {@link AuthPrompt} wrapper that can be used for cancellation and dismissal of the
+     * biometric prompt using {@link AuthPrompt#cancelAuthentication()}
+     */
+    @NonNull
+    public AuthPrompt startAuthentication() {
+        if (mCrypto == null) {
+            mBiometricPrompt.authenticate(mPromptInfo);
+        } else {
+            mBiometricPrompt.authenticate(mPromptInfo, mCrypto);
+        }
+
+        return new AuthPrompt() {
+            @Override
+            public void cancelAuthentication() {
+                mBiometricPrompt.cancelAuthentication();
+            }
+        };
+    }
+
+    /**
+     * Gets the subtitle for the prompt.
+     * @return subtitle for the prompt
+     */
+    @Nullable
+    public CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * Gets the description for the prompt. Defaults to null.
+     * @return description for the prompt
+     */
+    @Nullable
+    public CharSequence getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Indicates whether prompt requires explicit user confirmation after a passive biometric (e.g.
+     * iris or face) has been recognized but before
+     * {@link AuthPromptCallback#onAuthenticationSucceeded(androidx.fragment.app.FragmentActivity,
+     * BiometricPrompt.AuthenticationResult)} is called.
+     * @return whether prompt requires explicit user confirmation after a passive biometric.
+     */
+    public boolean isConfirmationRequired() {
+        return mIsConfirmationRequired;
+    }
+
+    /**
+     * Gets the crypto object for the prompt authentication.
+     * @return Crypto object associated with this authentication.
+     */
+    @Nullable
+    public BiometricPrompt.CryptoObject getCrypto() {
+        return mCrypto;
+    }
+
+    /**
+     * Builds a {@link BiometricPrompt} object for class 3 biometric only authentication with
+     * specified options.
+     */
+    public static final class Builder {
+
+        // Nullable options on the builder
+        @Nullable private BiometricPrompt.CryptoObject mCrypto = null;
+        @Nullable private CharSequence mSubtitle = null;
+        @Nullable private CharSequence mDescription = null;
+
+        // Non-null options on the builder.
+        @NonNull private final AuthPromptHost mAuthPromptHost;
+        @NonNull private final CharSequence mTitle;
+        @NonNull private final CharSequence mNegativeButtonText;
+        @NonNull private final Executor mClientExecutor;
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        @NonNull final AuthPromptCallback mClientCallback;
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        @NonNull final BiometricViewModel mViewModel;
+
+        private boolean mIsConfirmationRequired = true;
+
+        /**
+         * A builder used to set individual options for the {@link Class3BiometricAuthPrompt}
+         * class to construct a {@link BiometricPrompt} for class 3 biometric only authentication.
+         *
+         * @param authPromptHost Contains {@link androidx.fragment.app.Fragment} or
+         * {@link androidx.fragment.app.FragmentActivity} to host the authentication prompt
+         * @param title The title to be displayed on the prompt.
+         * @param negativeButtonText The label to be used for the negative button on the prompt.
+         * @param clientExecutor The executor that will run authentication callback methods.
+         * @param clientCallback The object that will receive and process authentication events.
+         */
+        public Builder(
+                @NonNull AuthPromptHost authPromptHost,
+                @NonNull CharSequence title,
+                @NonNull CharSequence negativeButtonText,
+                @NonNull Executor clientExecutor,
+                @NonNull AuthPromptCallback clientCallback) {
+            mAuthPromptHost = authPromptHost;
+            mTitle = title;
+            mNegativeButtonText = negativeButtonText;
+            mClientExecutor = clientExecutor;
+            mClientCallback = clientCallback;
+            mViewModel = new ViewModelProvider(mAuthPromptHost.getActivity())
+                    .get(BiometricViewModel.class);
+        }
+        /**
+         * A builder used to set individual options for the {@link Class3BiometricAuthPrompt}
+         * class to construct a {@link BiometricPrompt} for class 3 biometric only authentication.
+         * Sets mClientExecutor to new DefaultExecutor() object.
+         *
+         * @param authPromptHost Contains {@link androidx.fragment.app.Fragment} or
+         * {@link androidx.fragment.app.FragmentActivity} to host the authentication prompt
+         * @param title The title to be displayed on the prompt.
+         * @param negativeButtonText The label to be used for the negative button on the prompt.
+         * @param clientCallback The object that will receive and process authentication events.
+         */
+        public Builder(
+                @NonNull AuthPromptHost authPromptHost,
+                @NonNull CharSequence title,
+                @NonNull CharSequence negativeButtonText,
+                @NonNull AuthPromptCallback clientCallback) {
+            mAuthPromptHost = authPromptHost;
+            mTitle = title;
+            mNegativeButtonText = negativeButtonText;
+            mClientExecutor = new DefaultExecutor();
+            mClientCallback = clientCallback;
+            mViewModel = new ViewModelProvider(mAuthPromptHost.getActivity())
+                    .get(BiometricViewModel.class);
+        }
+
+        /**
+         * Required: Sets the crypto object for the prompt authentication. Defaults to null.
+         *
+         * @param crypto A crypto object to be associated with this authentication.
+         */
+        @NonNull
+        public Builder setCrypto(@NonNull BiometricPrompt.CryptoObject crypto) {
+            mCrypto = crypto;
+            return this;
+        }
+
+        /**
+         * Optional: Sets the subtitle for the prompt. Defaults to null.
+         *
+         * @param subtitle The subtitle to be displayed on the prompt.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setSubtitle(@NonNull CharSequence subtitle) {
+            mSubtitle = subtitle;
+            return this;
+        }
+
+        /**
+         * Optional: Sets the description for the prompt. Defaults to null.
+         *
+         * @param description The description to be displayed on the prompt.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setDescription(@NonNull CharSequence description) {
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Optional: Sets a system hint for whether to require explicit user confirmation after
+         * a passive biometric (e.g. iris or face) has been recognized but before
+         * {@link AuthPromptCallback#onAuthenticationSucceeded(
+         * androidx.fragment.app.FragmentActivity, BiometricPrompt.AuthenticationResult)} is
+         * called. Defaults to {@code true}.
+         *
+         * <p>Disabling this option is generally only appropriate for frequent, low-value
+         * transactions, such as re-authenticating for a previously authorized application.
+         *
+         * <p>Also note that, as it is merely a hint, this option may be ignored by the system.
+         * For example, the system may choose to instead always require confirmation if the user
+         * has disabled passive authentication for their device in Settings. Additionally, this
+         * option will be ignored on devices running OS versions prior to Android 10 (API 29).
+         *
+         * @param confirmationRequired Whether this option should be enabled.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setConfirmationRequired(boolean confirmationRequired) {
+            mIsConfirmationRequired = confirmationRequired;
+            return this;
+        }
+
+        /**
+         * Configures a {@link BiometricPrompt} object with the specified options, and returns
+         * a {@link Class3BiometricAuthPrompt} instance that can be used for starting
+         * authentication.
+         * @return {@link Class3BiometricAuthPrompt} instance for starting authentication.
+         */
+        @NonNull
+        public Class3BiometricAuthPrompt build() {
+            final BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo
+                    .Builder()
+                    .setTitle(mTitle)
+                    .setSubtitle(mSubtitle)
+                    .setDescription(mDescription)
+                    .setNegativeButtonText(mNegativeButtonText)
+                    .setConfirmationRequired(mIsConfirmationRequired)
+                    .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+                    .build();
+
+            final BiometricPrompt biometricPrompt;
+            BiometricPrompt.AuthenticationCallback wrappedCallback =
+                    new BiometricPrompt.AuthenticationCallback() {
+                        @Override
+                        public void onAuthenticationError(int errorCode,
+                                @NonNull CharSequence errString) {
+                            mClientCallback.onAuthenticationError(
+                                    mViewModel.getClientActivity(),
+                                    errorCode,
+                                    errString
+                            );
+                        }
+
+                        @Override
+                        public void onAuthenticationSucceeded(
+                                @NonNull BiometricPrompt.AuthenticationResult result) {
+                            mClientCallback.onAuthenticationSucceeded(
+                                    mViewModel.getClientActivity(),
+                                    result
+                            );
+                        }
+
+                        @Override
+                        public void onAuthenticationFailed() {
+                            mClientCallback.onAuthenticationFailed(
+                                    mViewModel.getClientActivity()
+                            );
+                        }
+                    };
+            if (mAuthPromptHost.getActivity() != null) {
+                biometricPrompt = new BiometricPrompt(mAuthPromptHost.getActivity(),
+                        mClientExecutor, wrappedCallback);
+            } else if (mAuthPromptHost.getFragment() != null) {
+                biometricPrompt = new BiometricPrompt(mAuthPromptHost.getFragment(),
+                        mClientExecutor, wrappedCallback);
+            } else {
+                throw new IllegalArgumentException("Invalid AuthPromptHost provided. Must "
+                        + "provide AuthPromptHost containing Fragment or FragmentActivity for"
+                        + " hosting the BiometricPrompt.");
+            }
+
+            return new Class3BiometricAuthPrompt(biometricPrompt, promptInfo, mCrypto,
+                    mSubtitle, mDescription, mIsConfirmationRequired);
+        }
+    }
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/auth/Class3BiometricOrCredentialAuthPrompt.java b/biometric/biometric/src/main/java/androidx/biometric/auth/Class3BiometricOrCredentialAuthPrompt.java
new file mode 100644
index 0000000..abb8248
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/auth/Class3BiometricOrCredentialAuthPrompt.java
@@ -0,0 +1,348 @@
+/*
+ * 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.biometric.auth;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.biometric.BiometricManager;
+import androidx.biometric.BiometricPrompt;
+import androidx.biometric.BiometricViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This class is used to build and configure a {@link BiometricPrompt} for authentication that
+ * permits Class 3 biometric modalities (fingerprint, iris, face, etc), or device credential
+ * modalities (device PIN, pattern, or password), and then start authentication.
+ *
+ * Class 3 (formerly known as Strong) refers to the strength of the biometric sensor, as specified
+ * in the Android 11 CDD. Class 3 authentication can be used for applications that use
+ * cryptographic operations.
+ */
+public class Class3BiometricOrCredentialAuthPrompt {
+
+    /**
+     * The default executor provided when not provided in the
+     * {@link Class3BiometricOrCredentialAuthPrompt} constructor.
+     */
+    private static class DefaultExecutor implements Executor {
+        private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        DefaultExecutor() {}
+
+        @Override
+        public void execute(Runnable runnable) {
+            mHandler.post(runnable);
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    @NonNull BiometricPrompt mBiometricPrompt;
+    @NonNull private BiometricPrompt.PromptInfo mPromptInfo;
+    private boolean mIsConfirmationRequired;
+
+    @Nullable private BiometricPrompt.CryptoObject mCrypto;
+    @Nullable private CharSequence mSubtitle;
+    @Nullable private CharSequence mDescription;
+
+    /**
+     * Constructs a {@link Class3BiometricOrCredentialAuthPrompt}, which can be used to begin
+     * authentication.
+     * @param biometricPrompt Manages a system-provided biometric prompt for authentication
+     * @param promptInfo A set of configurable options for how the {@link BiometricPrompt}
+     *                   should appear and behave.
+     * @param crypto A crypto object to be associated with this authentication.
+     * @param subtitle The subtitle to be displayed on the prompt.
+     * @param description The description to be displayed on the prompt.
+     * @param confirmationRequired Whether explicit user confirmation is required after a
+     *                             passive biometric
+     */
+    Class3BiometricOrCredentialAuthPrompt(@NonNull BiometricPrompt biometricPrompt,
+            @NonNull BiometricPrompt.PromptInfo promptInfo,
+            @Nullable BiometricPrompt.CryptoObject crypto,
+            @NonNull CharSequence subtitle,
+            @NonNull CharSequence description,
+            boolean confirmationRequired) {
+        mBiometricPrompt = biometricPrompt;
+        mPromptInfo = promptInfo;
+        mCrypto = crypto;
+        mSubtitle = subtitle;
+        mDescription = description;
+        mIsConfirmationRequired = confirmationRequired;
+    }
+
+    /**
+     * Begins authentication using the configured biometric prompt, and returns an
+     * {@link AuthPrompt} wrapper that can be used for cancellation and dismissal of the biometric
+     * prompt.
+     * @return {@link AuthPrompt} wrapper that can be used for cancellation and dismissal of the
+     * biometric prompt using {@link AuthPrompt#cancelAuthentication()}
+     */
+    @NonNull
+    public AuthPrompt startAuthentication() {
+        if (mCrypto == null) {
+            mBiometricPrompt.authenticate(mPromptInfo);
+        } else {
+            mBiometricPrompt.authenticate(mPromptInfo, mCrypto);
+        }
+
+        return new AuthPrompt() {
+            @Override
+            public void cancelAuthentication() {
+                mBiometricPrompt.cancelAuthentication();
+            }
+        };
+    }
+
+    /**
+     * Gets the subtitle for the prompt.
+     * @return subtitle for the prompt
+     */
+    @Nullable
+    public CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * Gets the description for the prompt. Defaults to null.
+     * @return description for the prompt
+     */
+    @Nullable
+    public CharSequence getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Indicates whether prompt requires explicit user confirmation after a passive biometric (e.g.
+     * iris or face) has been recognized but before {@link
+     * AuthPromptCallback#onAuthenticationSucceeded(androidx.fragment.app.FragmentActivity,
+     * BiometricPrompt.AuthenticationResult)} is called.
+     * @return whether prompt requires explicit user confirmation after a passive biometric.
+     */
+    public boolean isConfirmationRequired() {
+        return mIsConfirmationRequired;
+    }
+
+    /**
+     * Gets the crypto object for the prompt authentication.
+     * @return Crypto object associated with this authentication.
+     */
+    @Nullable
+    public BiometricPrompt.CryptoObject getCrypto() {
+        return mCrypto;
+    }
+
+    /**
+     * Builder to configure a {@link BiometricPrompt} object for class 3 biometric or device
+     * credential authentication with specified options.
+     */
+    public static final class Builder {
+        // Nullable options on the builder
+        @Nullable private BiometricPrompt.CryptoObject mCrypto = null;
+        @Nullable private CharSequence mSubtitle = null;
+        @Nullable private CharSequence mDescription = null;
+
+        // Non-null options on the builder
+        @NonNull private final AuthPromptHost mAuthPromptHost;
+        @NonNull private final CharSequence mTitle;
+        @NonNull private final Executor mClientExecutor;
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        @NonNull final AuthPromptCallback mClientCallback;
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        @NonNull final BiometricViewModel mViewModel;
+
+        private boolean mIsConfirmationRequired = true;
+
+        /**
+         * A builder used to set individual options for the
+         * {@link Class3BiometricOrCredentialAuthPrompt} class to construct a
+         * {@link BiometricPrompt} for class 3 biometric or device credential authentication.
+         *
+         * @param authPromptHost {@link androidx.fragment.app.Fragment} or
+         * {@link androidx.fragment.app.FragmentActivity} to host the authentication prompt
+         * @param title The title to be displayed on the prompt.
+         * @param clientExecutor The executor that will run authentication callback methods.
+         * @param clientCallback The object that will receive and process authentication events.
+         */
+        public Builder(
+                @NonNull AuthPromptHost authPromptHost,
+                @NonNull CharSequence title,
+                @NonNull Executor clientExecutor,
+                @NonNull AuthPromptCallback clientCallback) {
+            mAuthPromptHost = authPromptHost;
+            mTitle = title;
+            mClientExecutor = clientExecutor;
+            mClientCallback = clientCallback;
+            mViewModel = new ViewModelProvider(mAuthPromptHost.getActivity())
+                    .get(BiometricViewModel.class);
+        }
+
+        /**
+         * A builder used to set individual options for the
+         * {@link Class3BiometricOrCredentialAuthPrompt} class to construct a
+         * {@link BiometricPrompt} for class 3 biometric or device credential authentication.
+         * Sets mClientExecutor to new DefaultExecutor() object.
+         *
+         * @param authPromptHost {@link androidx.fragment.app.Fragment} or
+         * {@link androidx.fragment.app.FragmentActivity} to host the authentication prompt
+         * @param title The title to be displayed on the prompt.
+         * @param clientCallback The object that will receive and process authentication events.
+         */
+        public Builder(
+                @NonNull AuthPromptHost authPromptHost,
+                @NonNull CharSequence title,
+                @NonNull AuthPromptCallback clientCallback) {
+            mAuthPromptHost = authPromptHost;
+            mTitle = title;
+            mClientExecutor = new DefaultExecutor();
+            mClientCallback = clientCallback;
+            mViewModel = new ViewModelProvider(mAuthPromptHost.getActivity())
+                    .get(BiometricViewModel.class);
+        }
+
+        /**
+         * Optional: Sets the crypto object for the prompt.
+         * @param crypto A crypto object to be associated with this authentication.
+         */
+        @NonNull
+        public Builder setCrypto(@NonNull BiometricPrompt.CryptoObject crypto) {
+            mCrypto = crypto;
+            return this;
+        }
+
+        /**
+         * Optional: Sets the subtitle for the prompt.
+         *
+         * @param subtitle The subtitle to be displayed on the prompt.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setSubtitle(
+                @NonNull CharSequence subtitle) {
+            mSubtitle = subtitle;
+            return this;
+        }
+
+        /**
+         * Optional: Sets the description for the prompt.
+         *
+         * @param description The description to be displayed on the prompt.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setDescription(
+                @NonNull CharSequence description) {
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Optional: Sets a system hint for whether to require explicit user confirmation after
+         * a passive biometric (e.g. iris or face) has been recognized but before
+         * {@link AuthPromptCallback#onAuthenticationSucceeded(
+         * androidx.fragment.app.FragmentActivity, BiometricPrompt.AuthenticationResult)} is
+         * called. Defaults to {@code true}.
+         *
+         * <p>Disabling this option is generally only appropriate for frequent, low-value
+         * transactions, such as re-authenticating for a previously authorized application.
+         *
+         * <p>Also note that, as it is merely a hint, this option may be ignored by the system.
+         * For example, the system may choose to instead always require confirmation if the user
+         * has disabled passive authentication for their device in Settings. Additionally, this
+         * option will be ignored on devices running OS versions prior to Android 10 (API 29).
+         *
+         * @param confirmationRequired Whether this option should be enabled.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setConfirmationRequired(
+                boolean confirmationRequired) {
+            mIsConfirmationRequired = confirmationRequired;
+            return this;
+        }
+
+        /**
+         * Configures a {@link BiometricPrompt} object with the specified options, and returns a
+         * {@link Class3BiometricOrCredentialAuthPrompt} instance that can be used for starting
+         * authentication.
+         * @return {@link Class3BiometricOrCredentialAuthPrompt} instance for starting
+         * authentication.
+         */
+        @NonNull
+        public Class3BiometricOrCredentialAuthPrompt build() {
+            final BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo
+                    .Builder()
+                    .setTitle(mTitle)
+                    .setSubtitle(mSubtitle)
+                    .setDescription(mDescription)
+                    .setConfirmationRequired(mIsConfirmationRequired)
+                    .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG
+                            | BiometricManager.Authenticators.DEVICE_CREDENTIAL)
+                    .build();
+
+            final BiometricPrompt biometricPrompt;
+            BiometricPrompt.AuthenticationCallback wrappedCallback =
+                    new BiometricPrompt.AuthenticationCallback() {
+                        @Override
+                        public void onAuthenticationError(int errorCode,
+                                @NonNull CharSequence errString) {
+                            mClientCallback.onAuthenticationError(
+                                    mViewModel.getClientActivity(),
+                                    errorCode,
+                                    errString
+                            );
+                        }
+
+                        @Override
+                        public void onAuthenticationSucceeded(
+                                @NonNull BiometricPrompt.AuthenticationResult result) {
+                            mClientCallback.onAuthenticationSucceeded(
+                                    mViewModel.getClientActivity(),
+                                    result
+                            );
+                        }
+
+                        @Override
+                        public void onAuthenticationFailed() {
+                            mClientCallback.onAuthenticationFailed(
+                                    mViewModel.getClientActivity()
+                            );
+                        }
+                    };
+            if (mAuthPromptHost.getActivity() != null) {
+                biometricPrompt = new BiometricPrompt(mAuthPromptHost.getActivity(),
+                        mClientExecutor, wrappedCallback);
+            } else if (mAuthPromptHost.getFragment() != null) {
+                biometricPrompt = new BiometricPrompt(mAuthPromptHost.getFragment(),
+                        mClientExecutor, wrappedCallback);
+            } else {
+                throw new IllegalArgumentException("Invalid AuthPromptHost provided. Must "
+                        + "provide AuthPromptHost containing Fragment or FragmentActivity for"
+                        + " hosting the BiometricPrompt.");
+            }
+            return new Class3BiometricOrCredentialAuthPrompt(biometricPrompt, promptInfo, mCrypto,
+                    mSubtitle, mDescription, mIsConfirmationRequired);
+        }
+    }
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/auth/CredentialAuthPrompt.java b/biometric/biometric/src/main/java/androidx/biometric/auth/CredentialAuthPrompt.java
new file mode 100644
index 0000000..075d174
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/auth/CredentialAuthPrompt.java
@@ -0,0 +1,269 @@
+/*
+ * 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.biometric.auth;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.biometric.BiometricManager;
+import androidx.biometric.BiometricPrompt;
+import androidx.biometric.BiometricViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This class is used to build and configure a {@link BiometricPrompt} for authentication that
+ * only permits device credential modalities (device PIN, pattern, or password), and then start
+ * authentication.
+ */
+public class CredentialAuthPrompt {
+
+    /**
+     * The default executor provided when not provided in the {@link CredentialAuthPrompt}
+     * constructor.
+     */
+    private static class DefaultExecutor implements Executor {
+        private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        DefaultExecutor() {}
+
+        @Override
+        public void execute(Runnable runnable) {
+            mHandler.post(runnable);
+        }
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    @NonNull BiometricPrompt mBiometricPrompt;
+    @NonNull private BiometricPrompt.PromptInfo mPromptInfo;
+
+    @Nullable private BiometricPrompt.CryptoObject mCrypto;
+    @Nullable private CharSequence mDescription;
+
+    /**
+     * Constructs a {@link CredentialAuthPrompt}, which can be used to begin authentication.
+     * @param biometricPrompt Manages a system-provided biometric prompt for authentication
+     * @param promptInfo A set of configurable options for how the {@link BiometricPrompt}
+     *                   should appear and behave.
+     * @param crypto A crypto object to be associated with this authentication.
+     * @param description The description to be displayed on the prompt.
+     */
+    CredentialAuthPrompt(@NonNull BiometricPrompt biometricPrompt,
+            @NonNull BiometricPrompt.PromptInfo promptInfo,
+            @Nullable BiometricPrompt.CryptoObject crypto,
+            @NonNull CharSequence description) {
+        mBiometricPrompt = biometricPrompt;
+        mPromptInfo = promptInfo;
+        mCrypto = crypto;
+        mDescription = description;
+    }
+
+    /**
+     * Begins authentication using the configured authentication prompt, and returns an
+     * {@link AuthPrompt} wrapper that can be used for cancellation and dismissal of the
+     * authentication prompt.
+     * @return {@link AuthPrompt} wrapper that can be used for cancellation and dismissal of the
+     * authentication prompt using {@link AuthPrompt#cancelAuthentication()}
+     */
+    @NonNull
+    public AuthPrompt startAuthentication() {
+        if (mCrypto == null) {
+            mBiometricPrompt.authenticate(mPromptInfo);
+        } else {
+            mBiometricPrompt.authenticate(mPromptInfo, mCrypto);
+        }
+        return new AuthPrompt() {
+            @Override
+            public void cancelAuthentication() {
+                mBiometricPrompt.cancelAuthentication();
+            }
+        };
+    }
+
+    /**
+     * Returns the crypto object for the prompt.
+     * @return mCrypto A crypto object to be associated with this authentication.
+     */
+    @Nullable
+    public BiometricPrompt.CryptoObject getCrypto() {
+        return mCrypto;
+    }
+
+    /**
+     * Returns the description for the prompt.
+     * @return mDescription The description to be displayed on the prompt.
+     */
+    @Nullable
+    public CharSequence getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Builder to configure a {@link BiometricPrompt} object for device credential only
+     * authentication with specified options.
+     */
+    public static final class Builder {
+
+        // Mutable options to be set on the builder.
+        @Nullable private BiometricPrompt.CryptoObject mCrypto = null;
+        @Nullable private CharSequence mDescription = null;
+
+        @NonNull private final AuthPromptHost mAuthPromptHost;
+        @NonNull private final CharSequence mTitle;
+        @NonNull private final Executor mClientExecutor;
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        @NonNull final AuthPromptCallback mClientCallback;
+
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        @NonNull final BiometricViewModel mViewModel;
+
+        /**
+         * A builder used to set individual options for the {@link CredentialAuthPrompt}
+         * class to construct a {@link BiometricPrompt} for device credential authentication.
+         *
+         * @param authPromptHost {@link androidx.fragment.app.Fragment} or
+         * {@link androidx.fragment.app.FragmentActivity} to host the authentication prompt
+         * @param title The title to be displayed on the prompt.
+         * @param clientExecutor The executor that will run authentication callback methods.
+         * @param clientCallback The object that will receive and process authentication events.
+         * @return Builder object for setting remaining optional variables of the
+         * {@link CredentialAuthPrompt} class.
+         */
+        public Builder(
+                @NonNull AuthPromptHost authPromptHost,
+                @NonNull CharSequence title,
+                @NonNull Executor clientExecutor,
+                @NonNull AuthPromptCallback clientCallback) {
+            mAuthPromptHost = authPromptHost;
+            mTitle = title;
+            mClientExecutor = clientExecutor;
+            mClientCallback = clientCallback;
+            mViewModel = new ViewModelProvider(mAuthPromptHost.getActivity())
+                    .get(BiometricViewModel.class);
+        }
+
+        /**
+         * A builder used to set individual options for the {@link CredentialAuthPrompt}
+         * class to construct a {@link BiometricPrompt} for device credential authentication.
+         * Sets mClientExecutor to new DefaultExecutor() object.
+         *
+         * @param authPromptHost {@link androidx.fragment.app.Fragment} or
+         * {@link androidx.fragment.app.FragmentActivity} to host the authentication prompt
+         * @param title The title to be displayed on the prompt.
+         * @param clientCallback The object that will receive and process authentication events.
+         * @return Builder object for setting remaining optional variables of the
+         * {@link CredentialAuthPrompt} class.
+         */
+        public Builder(
+                @NonNull AuthPromptHost authPromptHost,
+                @NonNull CharSequence title,
+                @NonNull AuthPromptCallback clientCallback) {
+            mAuthPromptHost = authPromptHost;
+            mTitle = title;
+            mClientExecutor = new DefaultExecutor();
+            mClientCallback = clientCallback;
+            mViewModel = new ViewModelProvider(mAuthPromptHost.getActivity())
+                    .get(BiometricViewModel.class);
+        }
+
+        /**
+         * Optional: Sets the crypto object for the prompt.
+         * @param crypto A crypto object to be associated with this authentication.
+         */
+        @NonNull
+        public CredentialAuthPrompt.Builder setCrypto(
+                @NonNull BiometricPrompt.CryptoObject crypto
+        ) {
+            mCrypto = crypto;
+            return this;
+        }
+
+        /**
+         * Optional: Sets the description for the prompt. Defaults to null.
+         *
+         * @param description The description to be displayed on the prompt.
+         * @return This builder.
+         */
+        @NonNull
+        public CredentialAuthPrompt.Builder setDescription(@NonNull CharSequence description) {
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Configures a {@link BiometricPrompt} object with the specified options, and returns
+         * a {@link CredentialAuthPrompt} instance that can be used for starting authentication.
+         * @return {@link CredentialAuthPrompt} instance for starting authentication.
+         */
+        @NonNull
+        public CredentialAuthPrompt build() {
+            final BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo
+                    .Builder()
+                    .setTitle(mTitle)
+                    .setDescription(mDescription)
+                    .setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
+                    .build();
+
+            final BiometricPrompt biometricPrompt;
+            BiometricPrompt.AuthenticationCallback wrappedCallback =
+                    new BiometricPrompt.AuthenticationCallback() {
+                        @Override
+                        public void onAuthenticationError(int errorCode,
+                                @NonNull CharSequence errString) {
+                            mClientCallback.onAuthenticationError(
+                                    mViewModel.getClientActivity(),
+                                    errorCode,
+                                    errString
+                            );
+                        }
+
+                        @Override
+                        public void onAuthenticationSucceeded(
+                                @NonNull BiometricPrompt.AuthenticationResult result) {
+                            mClientCallback.onAuthenticationSucceeded(
+                                    mViewModel.getClientActivity(),
+                                    result
+                            );
+                        }
+
+                        @Override
+                        public void onAuthenticationFailed() {
+                            mClientCallback.onAuthenticationFailed(
+                                    mViewModel.getClientActivity()
+                            );
+                        }
+                    };
+            if (mAuthPromptHost.getActivity() != null) {
+                biometricPrompt = new BiometricPrompt(mAuthPromptHost.getActivity(),
+                        mClientExecutor, wrappedCallback);
+            } else if (mAuthPromptHost.getFragment() != null) {
+                biometricPrompt = new BiometricPrompt(mAuthPromptHost.getFragment(),
+                        mClientExecutor, wrappedCallback);
+            } else {
+                throw new IllegalArgumentException("Invalid AuthPromptHost provided. Must "
+                        + "provide AuthPromptHost containing Fragment or FragmentActivity for"
+                        + " hosting the BiometricPrompt.");
+            }
+            return new CredentialAuthPrompt(biometricPrompt, promptInfo, mCrypto, mDescription);
+        }
+    }
+}
diff --git a/buildSrc-tests/src/test/kotlin/androidx/build/XmlTestConfigVerificationTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/XmlTestConfigVerificationTest.kt
index 8011c6e..3017bed 100644
--- a/buildSrc-tests/src/test/kotlin/androidx/build/XmlTestConfigVerificationTest.kt
+++ b/buildSrc-tests/src/test/kotlin/androidx/build/XmlTestConfigVerificationTest.kt
@@ -41,4 +41,20 @@
         val parser = SAXParserFactory.newInstance().newSAXParser()
         parser.parse(InputSource(StringReader(SELF_INSTRUMENTING_TEMPLATE)), DefaultHandler())
     }
+
+    @Test
+    fun testValidTestConfigXml_MEDIA_TEMPLATE() {
+        val parser = SAXParserFactory.newInstance().newSAXParser()
+        parser.parse(
+            InputSource(
+                StringReader(
+                    MEDIA_TEMPLATE.replace(
+                        "INSTRUMENTATION_ARGS",
+                        CLIENT_PREVIOUS + SERVICE_PREVIOUS
+                    )
+                )
+            ),
+            DefaultHandler()
+        )
+    }
 }
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index e9f75e7..a7ae645 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -33,10 +33,6 @@
 import androidx.build.checkapi.LibraryApiTaskConfig
 import androidx.build.checkapi.configureProjectForApiTasks
 import androidx.build.studio.StudioTask
-import com.android.build.api.artifact.ArtifactType
-import com.android.build.api.artifact.Artifacts
-import com.android.build.api.dsl.ApplicationExtension
-import com.android.build.api.dsl.CommonExtension
 import com.android.build.gradle.AppExtension
 import com.android.build.gradle.AppPlugin
 import com.android.build.gradle.LibraryExtension
@@ -521,43 +517,7 @@
             Jacoco.registerClassFilesTask(project, this)
         }
 
-        val commonExtension = project.extensions.getByType(CommonExtension::class.java)
-        try {
-            val androidComponentsExtensionType =
-                Class.forName("com.android.build.api.extension.AndroidComponentsExtension")
-            val androidComponentsExtension =
-                project.extensions.getByType(androidComponentsExtensionType)
-            val selectorType = Class.forName("com.android.build.api.extension.VariantSelector")
-            val selector = androidComponentsExtensionType.getMethod("selector")
-                .invoke(androidComponentsExtension)
-            androidComponentsExtension.javaClass.getMethod(
-                "androidTest",
-                selectorType,
-                Function1::class.java
-            ).invoke(
-                androidComponentsExtension,
-                selector.javaClass.getMethod("all").invoke(selector),
-                { androidTest: Any ->
-                    createTestConfigurationGenerationTask(
-                        project,
-                        androidTest.javaClass.getMethod(
-                            "getName"
-                        ).invoke(
-                            androidTest
-                        ) as String,
-                        androidTest.javaClass.getMethod(
-                            "getArtifacts"
-                        ).invoke(
-                            androidTest
-                        ) as Artifacts,
-                        defaultConfig.minSdk!!,
-                        defaultConfig.testInstrumentationRunner!!
-                    )
-                }
-            )
-        } catch (cnfe: ClassNotFoundException) {
-            commonExtension.configureTestConfigGeneration(project)
-        }
+        this.configureTestConfigGenerationViaReflection(project)
 
         val buildTestApksTask = project.rootProject.tasks.named(BUILD_TEST_APKS_TASK)
         testVariants.all { variant ->
@@ -568,118 +528,6 @@
         }
     }
 
-    private fun CommonExtension<*, *, *, *, *, *, *, *>
-    .configureTestConfigGeneration(project: Project) {
-        // old iteration of the new API.
-        javaClass.getMethod("onVariants", Function1::class.java)
-            .invoke(
-                this,
-                { variant: Any ->
-                    variant.javaClass.getMethod(
-                        "androidTestProperties",
-                        Function1::class.java
-                    ).invoke(
-                        variant,
-                        { androidTest: Any ->
-                            createTestConfigurationGenerationTask(
-                                project,
-                                androidTest.javaClass.getMethod(
-                                    "getName"
-                                ).invoke(androidTest) as String,
-                                androidTest.javaClass.getMethod(
-                                    "getArtifacts"
-                                ).invoke(androidTest) as Artifacts,
-                                defaultConfig.minSdk!!,
-                                defaultConfig.testInstrumentationRunner!!
-                            )
-                        }
-                    )
-                } as Function1<Any, Any>
-            )
-    }
-
-    private fun createTestConfigurationGenerationTask(
-        project: Project,
-        variantName: String,
-        artifacts: Artifacts,
-        minSdk: Int,
-        testRunner: String
-    ) {
-        val generateTestConfigurationTask = project.tasks.register(
-            "${GENERATE_TEST_CONFIGURATION_TASK}$variantName",
-            GenerateTestConfigurationTask::class.java
-        ) {
-            it.testFolder.set(artifacts.get(ArtifactType.APK))
-            it.testLoader.set(artifacts.getBuiltArtifactsLoader())
-            it.outputXml.fileValue(
-                File(
-                    project.getTestConfigDirectory(),
-                    "${project.path.asFilenamePrefix()}$variantName.xml"
-                )
-            )
-            it.minSdk.set(minSdk)
-            it.hasBenchmarkPlugin.set(project.hasBenchmarkPlugin())
-            it.testRunner.set(testRunner)
-            it.projectPath.set(project.path)
-            AffectedModuleDetector.configureTaskGuard(it)
-        }
-        // Disable xml generation for projects that have no test sources
-        project.afterEvaluate {
-            generateTestConfigurationTask.configure {
-                it.enabled = hasAndroidTestSourceCode(project)
-            }
-        }
-        project.rootProject.tasks.findByName(ZIP_TEST_CONFIGS_WITH_APKS_TASK)!!
-            .dependsOn(generateTestConfigurationTask)
-    }
-
-    private fun ApplicationExtension<*, *, *, *, *>
-    .addAppApkToTestConfigGeneration(project: Project) {
-        val allVariants = javaClass.getMethod("getOnVariantProperties")
-            .invoke(this)
-
-        allVariants.javaClass.getMethod("withBuildType", String::class.java, Function1::class.java)
-            .invoke(
-                allVariants,
-                "debug",
-                { debugVariant: Any ->
-                    val artifacts = debugVariant.javaClass.getMethod("getArtifacts")
-                        .invoke(debugVariant) as Artifacts
-                    project.tasks.withType(GenerateTestConfigurationTask::class.java) {
-                        it.appFolder.set(artifacts.get(ArtifactType.APK))
-                        it.appLoader.set(artifacts.getBuiltArtifactsLoader())
-                    }
-                }
-            )
-    }
-
-    /**
-     * Expected to be called in afterEvaluate when all extensions are available
-     */
-    private fun hasAndroidTestSourceCode(project: Project): Boolean {
-        // check Java androidTest source set
-        project.extensions.findByType(TestedExtension::class.java)?.sourceSets
-            ?.findByName("androidTest")?.let { sourceSet ->
-                // using getSourceFiles() instead of sourceFiles due to b/150800094
-                if (!sourceSet.java.getSourceFiles().isEmpty) return true
-            }
-
-        // check kotlin-android androidTest source set
-        project.extensions.findByType(KotlinAndroidProjectExtension::class.java)
-            ?.sourceSets?.findByName("androidTest")?.let {
-                if (it.kotlin.files.isNotEmpty()) return true
-            }
-
-        // check kotlin-multiplatform androidAndroidTest source set
-        project.multiplatformExtension?.apply {
-            sourceSets.findByName("androidAndroidTest")?.let {
-                if (it.kotlin.files.isNotEmpty()) return true
-            }
-        }
-
-        return false
-    }
-
     private fun ApkVariant.configureApkCopy(
         project: Project,
         testApk: Boolean
@@ -687,7 +535,7 @@
         packageApplicationProvider.get().let { packageTask ->
             AffectedModuleDetector.configureTaskGuard(packageTask)
             // Skip copying AndroidTest apks if they have no source code (no tests to run).
-            if (testApk && !hasAndroidTestSourceCode(project)) {
+            if (testApk && !project.hasAndroidTestSourceCode()) {
                 return
             }
 
@@ -777,38 +625,7 @@
             }
         }
 
-        try {
-            val androidComponentsExtensionType = Class.forName(
-                "com.android.build.api.extension.ApplicationAndroidComponentsExtension"
-            )
-            val androidComponentsExtension =
-                project.extensions.getByType(androidComponentsExtensionType)
-            val selectorType = Class.forName(
-                "com.android.build.api.extension.VariantSelector"
-            )
-            val selector = androidComponentsExtensionType.getMethod("selector")
-                .invoke(androidComponentsExtension)
-            androidComponentsExtensionType
-                .getMethod("onVariants", selectorType, Function1::class.java)
-                .invoke(
-                    androidComponentsExtension,
-                    selectorType.getMethod("withBuildType", String::class.java)
-                        .invoke(selector, "debug"),
-                    { debugVariant: Any ->
-                        val artifacts = debugVariant.javaClass.getMethod("getArtifacts")
-                            .invoke(debugVariant) as Artifacts
-                        project.tasks.withType(GenerateTestConfigurationTask::class.java) {
-                            it.appFolder.set(artifacts.get(ArtifactType.APK))
-                            it.appLoader.set(artifacts.getBuiltArtifactsLoader())
-                        }
-                    }
-                )
-        } catch (cnfe: ClassNotFoundException) {
-            val applicationExtension = project.extensions.getByType(
-                ApplicationExtension::class.java
-            )
-            applicationExtension.addAppApkToTestConfigGeneration(project)
-        }
+        project.addAppApkToTestConfigGenerationViaReflection()
 
         val buildTestApksTask = project.rootProject.tasks.named(BUILD_TEST_APKS_TASK)
         applicationVariants.all { variant ->
@@ -1015,16 +832,18 @@
  * to determine what gets run by our test runner
  */
 fun String.renameApkForTesting(projectPath: String, hasBenchmarkPlugin: Boolean): String {
-    return if (this.contains("media-test") || this.contains("media2-test")) {
-        // Exclude media-test-* and media2-test-* modules from
-        // existing support library presubmit tests.
-        this.replace("-debug-androidTest", "")
-    } else if (hasBenchmarkPlugin) {
-        val name = this.replace("-androidTest", "-androidBenchmark")
-        "${projectPath.asFilenamePrefix()}_$name"
-    } else {
-        "${projectPath.asFilenamePrefix()}_$this"
-    }
+
+    val name =
+        if (projectPath.contains("media") && projectPath.contains("version-compat-tests")) {
+            // Exclude media*:version-compat-tests modules from
+            // existing support library presubmit tests.
+            this.replace("-debug-androidTest", "")
+        } else if (hasBenchmarkPlugin) {
+            this.replace("-androidTest", "-androidBenchmark")
+        } else {
+            this
+        }
+    return "${projectPath.asFilenamePrefix()}_$name"
 }
 
 fun Project.hasBenchmarkPlugin(): Boolean {
@@ -1048,3 +867,30 @@
         it.dependsOn(task)
     }
 }
+
+/**
+ * Expected to be called in afterEvaluate when all extensions are available
+ */
+internal fun Project.hasAndroidTestSourceCode(): Boolean {
+    // check Java androidTest source set
+    this.extensions.findByType(TestedExtension::class.java)!!.sourceSets
+        .findByName("androidTest")?.let { sourceSet ->
+            // using getSourceFiles() instead of sourceFiles due to b/150800094
+            if (!sourceSet.java.getSourceFiles().isEmpty) return true
+        }
+
+    // check kotlin-android androidTest source set
+    this.extensions.findByType(KotlinAndroidProjectExtension::class.java)
+        ?.sourceSets?.findByName("androidTest")?.let {
+            if (it.kotlin.files.isNotEmpty()) return true
+        }
+
+    // check kotlin-multiplatform androidAndroidTest source set
+    this.multiplatformExtension?.apply {
+        sourceSets.findByName("androidAndroidTest")?.let {
+            if (it.kotlin.files.isNotEmpty()) return true
+        }
+    }
+
+    return false
+}
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
index 309f9b4..9847fce 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
@@ -161,8 +161,14 @@
                     subproject.name != "docs-tip-of-tree" &&
                     subproject.name != "camera-testapp-timing" &&
                     subproject.name != "room-testapp" &&
-                    subproject.name != "support-media2-test-client-previous" &&
-                    subproject.name != "support-media2-test-service-previous"
+                    !(
+                        subproject.path.contains
+                        ("media2:media2-session:version-compat-tests:client-previous")
+                        ) &&
+                    !(
+                        subproject.path.contains
+                        ("media2:media2-session:version-compat-tests:service-previous")
+                        )
                 ) {
                     subproject.configurations.all { configuration ->
                         configuration.resolutionStrategy.dependencySubstitution.apply {
diff --git a/buildSrc/src/main/kotlin/androidx/build/GenerateMediaTestConfigurationTask.kt b/buildSrc/src/main/kotlin/androidx/build/GenerateMediaTestConfigurationTask.kt
new file mode 100644
index 0000000..0bda7fe
--- /dev/null
+++ b/buildSrc/src/main/kotlin/androidx/build/GenerateMediaTestConfigurationTask.kt
@@ -0,0 +1,219 @@
+/*
+ * 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.build
+
+import com.android.build.api.variant.BuiltArtifacts
+import com.android.build.api.variant.BuiltArtifactsLoader
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+import java.io.File
+
+const val MEDIA_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?>
+        <!-- Copyright (C) 2019 The Android Open Source Project
+        Licensed under the Apache License, Version 2.0 (the "License")
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+        http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+        -->
+        <configuration description="Runs tests for the module">
+        <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MinApiLevelModuleController">
+            <option name="min-api-level" value="MIN_SDK" />
+        </object>
+        <option name="test-suite-tag" value="androidx_unit_tests_suite" />
+        <option name="config-descriptor:metadata" key="applicationId"
+            value="CLIENT_APPLICATION_ID;SERVICE_APPLICATION_ID" />
+        <option name="wifi:disable" value="true" />
+        <include name="google/unbundled/common/setup" />
+        <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CLIENT_FILE_NAME" />
+        <option name="test-file-name" value="SERVICE_FILE_NAME" />
+        </target_preparer>
+        <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="runner" value="TEST_RUNNER"/>
+        <option name="package" value="CLIENT_APPLICATION_ID" />
+        INSTRUMENTATION_ARGS
+        </test>
+        <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="runner" value="TEST_RUNNER"/>
+        <option name="package" value="SERVICE_APPLICATION_ID" />
+        INSTRUMENTATION_ARGS
+        </test>
+        </configuration>"""
+
+const val CLIENT_PREVIOUS = """
+    <option name="instrumentation-arg" value="client_version=previous" />
+"""
+const val CLIENT_TOT = """
+    <option name="instrumentation-arg" value="client_version=tot" />
+"""
+const val SERVICE_PREVIOUS = """
+    <option name="instrumentation-arg" value="service_version=previous" />
+"""
+const val SERVICE_TOT = """
+    <option name="instrumentation-arg" value="service_version=tot" />
+"""
+
+/**
+ * Writes three configuration files to test combinations of media client & service in
+ * <a href=https://source.android.com/devices/tech/test_infra/tradefed/testing/through-suite/android-test-structure>AndroidTest.xml</a>
+ * format that gets zipped alongside the APKs to be tested. The combinations are of previous and
+ * tip-of-tree versions client and service. We want to test every possible pairing that includes
+ * tip-of-tree.
+ *
+ * This config gets ingested by Tradefed.
+ */
+abstract class GenerateMediaTestConfigurationTask : DefaultTask() {
+
+    @get:InputFiles
+    abstract val clientToTFolder: DirectoryProperty
+
+    @get:Internal
+    abstract val clientToTLoader: Property<BuiltArtifactsLoader>
+
+    @get:InputFiles
+    abstract val clientPreviousFolder: DirectoryProperty
+
+    @get:Internal
+    abstract val clientPreviousLoader: Property<BuiltArtifactsLoader>
+
+    @get:InputFiles
+    abstract val serviceToTFolder: DirectoryProperty
+
+    @get:Internal
+    abstract val serviceToTLoader: Property<BuiltArtifactsLoader>
+
+    @get:InputFiles
+    abstract val servicePreviousFolder: DirectoryProperty
+
+    @get:Internal
+    abstract val servicePreviousLoader: Property<BuiltArtifactsLoader>
+
+    @get:Input
+    abstract val clientToTPath: Property<String>
+
+    @get:Input
+    abstract val clientPreviousPath: Property<String>
+
+    @get:Input
+    abstract val serviceToTPath: Property<String>
+
+    @get:Input
+    abstract val servicePreviousPath: Property<String>
+
+    @get:Input
+    abstract val minSdk: Property<Int>
+
+    @get:Input
+    abstract val testRunner: Property<String>
+
+    @get:OutputFile
+    abstract val clientPreviousServiceToT: RegularFileProperty
+
+    @get:OutputFile
+    abstract val clientToTServicePrevious: RegularFileProperty
+
+    @get:OutputFile
+    abstract val clientToTServiceToT: RegularFileProperty
+
+    @TaskAction
+    fun generateAndroidTestZip() {
+        val clientToTApk = resolveApk(clientToTFolder, clientToTLoader)
+        val clientPreviousApk = resolveApk(clientPreviousFolder, clientPreviousLoader)
+        val serviceToTApk = resolveApk(serviceToTFolder, serviceToTLoader)
+        val servicePreviousApk = resolveApk(
+            servicePreviousFolder, servicePreviousLoader
+        )
+        writeConfigFileContent(
+            clientToTApk, serviceToTApk, clientToTPath.get(),
+            serviceToTPath.get(), clientToTServiceToT
+        )
+        writeConfigFileContent(
+            clientToTApk, servicePreviousApk, clientToTPath.get(),
+            servicePreviousPath.get(), clientToTServicePrevious
+        )
+        writeConfigFileContent(
+            clientPreviousApk, serviceToTApk, clientPreviousPath.get(),
+            serviceToTPath.get(), clientPreviousServiceToT
+        )
+    }
+
+    private fun resolveApk(
+        apkFolder: DirectoryProperty,
+        apkLoader: Property<BuiltArtifactsLoader>
+    ): BuiltArtifacts {
+        return apkLoader.get().load(apkFolder.get())
+            ?: throw RuntimeException("Cannot load APK for $name")
+    }
+
+    private fun resolveName(apk: BuiltArtifacts, path: String): String {
+        return apk.elements.single().outputFile.substringAfterLast("/")
+            .renameApkForTesting(path, false)
+    }
+
+    private fun writeConfigFileContent(
+        clientApk: BuiltArtifacts,
+        serviceApk: BuiltArtifacts,
+        clientPath: String,
+        servicePath: String,
+        outputFile: RegularFileProperty
+    ) {
+        val instrumentationArgs =
+            if (clientPath.contains("previous")) {
+                if (servicePath.contains("previous")) {
+                    CLIENT_PREVIOUS + SERVICE_PREVIOUS
+                } else {
+                    CLIENT_PREVIOUS + SERVICE_TOT
+                }
+            } else if (servicePath.contains("previous")) {
+                CLIENT_TOT + SERVICE_PREVIOUS
+            } else {
+                CLIENT_TOT + SERVICE_TOT
+            }
+        var configContent: String = MEDIA_TEMPLATE
+        configContent = configContent
+            .replace("CLIENT_FILE_NAME", resolveName(clientApk, clientPath))
+            .replace("SERVICE_FILE_NAME", resolveName(serviceApk, servicePath))
+            .replace("CLIENT_APPLICATION_ID", clientApk.applicationId)
+            .replace("SERVICE_APPLICATION_ID", serviceApk.applicationId)
+            .replace("MIN_SDK", minSdk.get().toString())
+            .replace("TEST_RUNNER", testRunner.get())
+            .replace("INSTRUMENTATION_ARGS", instrumentationArgs)
+        val resolvedOutputFile: File = outputFile.asFile.get()
+        if (!resolvedOutputFile.exists()) {
+            if (!resolvedOutputFile.createNewFile()) {
+                throw RuntimeException(
+                    "Failed to create test configuration file: $outputFile"
+                )
+            }
+        }
+        resolvedOutputFile.writeText(configContent)
+    }
+}
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 378205f..58d463f 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -82,7 +82,7 @@
     val MEDIAROUTER = Version("1.3.0-alpha01")
     val NAVIGATION = Version("2.4.0-alpha01")
     val NAVIGATION_COMPOSE = Version("1.0.0-alpha03")
-    val PAGING = Version("3.0.0-alpha09")
+    val PAGING = Version("3.0.0-alpha10")
     val PAGING_COMPOSE = Version("1.0.0-alpha03")
     val PALETTE = Version("1.1.0-alpha01")
     val PRINT = Version("1.1.0-beta01")
diff --git a/buildSrc/src/main/kotlin/androidx/build/MavenUploadHelper.kt b/buildSrc/src/main/kotlin/androidx/build/MavenUploadHelper.kt
index e460eb0..be665d8 100644
--- a/buildSrc/src/main/kotlin/androidx/build/MavenUploadHelper.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/MavenUploadHelper.kt
@@ -150,7 +150,7 @@
         ?: throw Exception("You must specify mavenGroup for $name project")
     val strippedGroupId = mavenGroup.substringAfterLast(".")
     if (mavenGroup.startsWith("androidx") && !name.startsWith(strippedGroupId)) {
-        throw Exception("Your artifactId must start with $strippedGroupId! (currently is $name)")
+        throw Exception("Your artifactId must start with '$strippedGroupId'. (currently is $name)")
     }
     return mavenGroup
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/TestSuiteConfiguration.kt b/buildSrc/src/main/kotlin/androidx/build/TestSuiteConfiguration.kt
new file mode 100644
index 0000000..dea292a
--- /dev/null
+++ b/buildSrc/src/main/kotlin/androidx/build/TestSuiteConfiguration.kt
@@ -0,0 +1,342 @@
+/*
+ * 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.build
+
+import androidx.build.dependencyTracker.AffectedModuleDetector
+import com.android.build.api.artifact.ArtifactType
+import com.android.build.api.artifact.Artifacts
+import com.android.build.api.dsl.ApplicationExtension
+import com.android.build.api.dsl.CommonExtension
+import com.android.build.gradle.TestedExtension
+import org.gradle.api.Project
+import org.gradle.api.tasks.TaskProvider
+import java.io.File
+
+fun Project.createTestConfigurationGenerationTask(
+    variantName: String,
+    artifacts: Artifacts,
+    minSdk: Int,
+    testRunner: String
+) {
+    val generateTestConfigurationTask = this.tasks.register(
+        "${AndroidXPlugin.GENERATE_TEST_CONFIGURATION_TASK}$variantName",
+        GenerateTestConfigurationTask::class.java
+    ) {
+        it.testFolder.set(artifacts.get(ArtifactType.APK))
+        it.testLoader.set(artifacts.getBuiltArtifactsLoader())
+        it.outputXml.fileValue(
+            File(
+                this.getTestConfigDirectory(),
+                "${this.path.asFilenamePrefix()}$variantName.xml"
+            )
+        )
+        it.minSdk.set(minSdk)
+        it.hasBenchmarkPlugin.set(this.hasBenchmarkPlugin())
+        it.testRunner.set(testRunner)
+        it.projectPath.set(this.path)
+        AffectedModuleDetector.configureTaskGuard(it)
+    }
+    // Disable xml generation for projects that have no test sources
+    this.afterEvaluate {
+        generateTestConfigurationTask.configure {
+            it.enabled = this.hasAndroidTestSourceCode()
+        }
+    }
+    this.rootProject.tasks.findByName(AndroidXPlugin.ZIP_TEST_CONFIGS_WITH_APKS_TASK)!!
+        .dependsOn(generateTestConfigurationTask)
+}
+
+fun Project.addAppApkToTestConfigGenerationViaReflection() {
+    try {
+        val androidComponentsExtensionType = Class.forName(
+            "com.android.build.api.extension.ApplicationAndroidComponentsExtension"
+        )
+        val androidComponentsExtension =
+            this.extensions.getByType(androidComponentsExtensionType)
+        val selectorType = Class.forName(
+            "com.android.build.api.extension.VariantSelector"
+        )
+        val selector = androidComponentsExtensionType.getMethod("selector")
+            .invoke(androidComponentsExtension)
+        androidComponentsExtensionType
+            .getMethod("onVariants", selectorType, Function1::class.java)
+            .invoke(
+                androidComponentsExtension,
+                selectorType.getMethod("withBuildType", String::class.java)
+                    .invoke(selector, "debug"),
+                { debugVariant: Any ->
+                    val artifacts = debugVariant.javaClass.getMethod("getArtifacts")
+                        .invoke(debugVariant) as Artifacts
+                    this.tasks.withType(GenerateTestConfigurationTask::class.java) {
+                        it.appFolder.set(artifacts.get(ArtifactType.APK))
+                        it.appLoader.set(artifacts.getBuiltArtifactsLoader())
+                    }
+                }
+            )
+    } catch (cnfe: ClassNotFoundException) {
+        val applicationExtension = this.extensions.getByType(
+            ApplicationExtension::class.java
+        )
+        applicationExtension.addAppApkToTestConfigGeneration(this)
+    }
+}
+
+fun ApplicationExtension<*, *, *, *, *>
+.addAppApkToTestConfigGeneration(project: Project) {
+    val allVariants = javaClass.getMethod("getOnVariantProperties")
+        .invoke(this)
+
+    allVariants.javaClass.getMethod("withBuildType", String::class.java, Function1::class.java)
+        .invoke(
+            allVariants,
+            "debug",
+            { debugVariant: Any ->
+                val artifacts = debugVariant.javaClass.getMethod("getArtifacts")
+                    .invoke(debugVariant) as Artifacts
+                project.tasks.withType(GenerateTestConfigurationTask::class.java) {
+                    it.appFolder.set(artifacts.get(ArtifactType.APK))
+                    it.appLoader.set(artifacts.getBuiltArtifactsLoader())
+                }
+            }
+        )
+}
+
+private fun getOrCreateMediaTestConfigTask(project: Project, isMedia2: Boolean):
+    TaskProvider<GenerateMediaTestConfigurationTask> {
+        val mediaPrefix = getMediaConfigTaskPrefix(isMedia2)
+        if (!project.parent!!.tasks.withType(GenerateMediaTestConfigurationTask::class.java)
+            .names.contains(
+                    "support-$mediaPrefix-test${
+                    AndroidXPlugin
+                        .GENERATE_TEST_CONFIGURATION_TASK
+                    }"
+                )
+        ) {
+            val task = project.parent!!.tasks.register(
+                "support-$mediaPrefix-test${AndroidXPlugin.GENERATE_TEST_CONFIGURATION_TASK}",
+                GenerateMediaTestConfigurationTask::class.java
+            )
+            project.rootProject.tasks.findByName(AndroidXPlugin.ZIP_TEST_CONFIGS_WITH_APKS_TASK)!!
+                .dependsOn(task)
+            return task
+        } else {
+            return project.parent!!.tasks.withType(GenerateMediaTestConfigurationTask::class.java)
+                .named(
+                    "support-$mediaPrefix-test${
+                    AndroidXPlugin
+                        .GENERATE_TEST_CONFIGURATION_TASK
+                    }"
+                )
+        }
+    }
+
+private fun getMediaConfigTaskPrefix(isMedia2: Boolean): String {
+    return if (isMedia2) "media2" else "media"
+}
+
+fun Project.createOrUpdateMediaTestConfigurationGenerationTask(
+    variantName: String,
+    artifacts: Artifacts,
+    minSdk: Int,
+    testRunner: String,
+    isMedia2: Boolean
+) {
+    val mediaPrefix = getMediaConfigTaskPrefix(isMedia2)
+    val mediaTask = getOrCreateMediaTestConfigTask(this, isMedia2)
+    mediaTask.configure {
+        it as GenerateMediaTestConfigurationTask
+        if (this.name.contains("client")) {
+            if (this.name.contains("previous")) {
+                it.clientPreviousFolder.set(artifacts.get(ArtifactType.APK))
+                it.clientPreviousLoader.set(artifacts.getBuiltArtifactsLoader())
+                it.clientPreviousPath.set(this.path)
+            } else {
+                it.clientToTFolder.set(artifacts.get(ArtifactType.APK))
+                it.clientToTLoader.set(artifacts.getBuiltArtifactsLoader())
+                it.clientToTPath.set(this.path)
+            }
+        } else {
+            if (this.name.contains("previous")) {
+                it.servicePreviousFolder.set(artifacts.get(ArtifactType.APK))
+                it.servicePreviousLoader.set(artifacts.getBuiltArtifactsLoader())
+                it.servicePreviousPath.set(this.path)
+            } else {
+                it.serviceToTFolder.set(artifacts.get(ArtifactType.APK))
+                it.serviceToTLoader.set(artifacts.getBuiltArtifactsLoader())
+                it.serviceToTPath.set(this.path)
+            }
+        }
+        it.clientPreviousServiceToT.fileValue(
+            File(
+                this.getTestConfigDirectory(),
+                "${mediaPrefix}ClientPreviousServiceToT$variantName.xml"
+            )
+        )
+        it.clientToTServicePrevious.fileValue(
+            File(
+                this.getTestConfigDirectory(),
+                "${mediaPrefix}ClientToTServicePrevious$variantName.xml"
+            )
+        )
+        it.clientToTServiceToT.fileValue(
+            File(
+                this.getTestConfigDirectory(),
+                "${mediaPrefix}ClientToTServiceToT$variantName.xml"
+            )
+        )
+        it.minSdk.set(minSdk)
+        it.testRunner.set(testRunner)
+        AffectedModuleDetector.configureTaskGuard(it)
+    }
+}
+
+fun CommonExtension<*, *, *, *, *, *, *, *>
+.configureTestConfigGeneration(project: Project) {
+    // old iteration of the new API.
+    javaClass.getMethod("onVariants", Function1::class.java)
+        .invoke(
+            this,
+            { variant: Any ->
+                variant.javaClass.getMethod(
+                    "androidTestProperties",
+                    Function1::class.java
+                ).invoke(
+                    variant,
+                    { androidTest: Any ->
+                        when {
+                            project.path.contains("media2:version-compat-tests") -> {
+                                project.createOrUpdateMediaTestConfigurationGenerationTask(
+                                    androidTest.javaClass.getMethod(
+                                        "getName"
+                                    ).invoke(androidTest) as String,
+                                    androidTest.javaClass.getMethod(
+                                        "getArtifacts"
+                                    ).invoke(androidTest) as Artifacts,
+                                    defaultConfig.minSdk!!,
+                                    defaultConfig.testInstrumentationRunner!!,
+                                    true
+                                )
+                            }
+                            project.path.contains("media:version-compat-tests") -> {
+                                project.createOrUpdateMediaTestConfigurationGenerationTask(
+                                    androidTest.javaClass.getMethod(
+                                        "getName"
+                                    ).invoke(androidTest) as String,
+                                    androidTest.javaClass.getMethod(
+                                        "getArtifacts"
+                                    ).invoke(androidTest) as Artifacts,
+                                    defaultConfig.minSdk!!,
+                                    defaultConfig.testInstrumentationRunner!!,
+                                    false
+                                )
+                            }
+                            else -> {
+                                project.createTestConfigurationGenerationTask(
+                                    androidTest.javaClass.getMethod(
+                                        "getName"
+                                    ).invoke(androidTest) as String,
+                                    androidTest.javaClass.getMethod(
+                                        "getArtifacts"
+                                    ).invoke(androidTest) as Artifacts,
+                                    defaultConfig.minSdk!!,
+                                    defaultConfig.testInstrumentationRunner!!
+                                )
+                            }
+                        }
+                    }
+                )
+            } as Function1<Any, Any>
+        )
+}
+
+fun TestedExtension.configureTestConfigGenerationViaReflection(project: Project) {
+    val commonExtension = project.extensions.getByType(CommonExtension::class.java)
+    try {
+        val androidComponentsExtensionType =
+            Class.forName("com.android.build.api.extension.AndroidComponentsExtension")
+        val androidComponentsExtension =
+            project.extensions.getByType(androidComponentsExtensionType)
+        val selectorType = Class.forName("com.android.build.api.extension.VariantSelector")
+        val selector = androidComponentsExtensionType.getMethod("selector")
+            .invoke(androidComponentsExtension)
+        androidComponentsExtension.javaClass.getMethod(
+            "androidTest",
+            selectorType,
+            Function1::class.java
+        ).invoke(
+            androidComponentsExtension,
+            selector.javaClass.getMethod("all").invoke(selector),
+            { androidTest: Any ->
+                when {
+                    // support-media tests are special cased
+                    project.path.contains("media2:version-compat-tests") -> {
+                        project.createOrUpdateMediaTestConfigurationGenerationTask(
+                            androidTest.javaClass.getMethod(
+                                "getName"
+                            ).invoke(
+                                androidTest
+                            ) as String,
+                            androidTest.javaClass.getMethod(
+                                "getArtifacts"
+                            ).invoke(
+                                androidTest
+                            ) as Artifacts,
+                            defaultConfig.minSdk!!,
+                            defaultConfig.testInstrumentationRunner!!,
+                            true
+                        )
+                    }
+                    project.path.contains("media:version-compat-tests") -> {
+                        project.createOrUpdateMediaTestConfigurationGenerationTask(
+                            androidTest.javaClass.getMethod(
+                                "getName"
+                            ).invoke(
+                                androidTest
+                            ) as String,
+                            androidTest.javaClass.getMethod(
+                                "getArtifacts"
+                            ).invoke(
+                                androidTest
+                            ) as Artifacts,
+                            defaultConfig.minSdk!!,
+                            defaultConfig.testInstrumentationRunner!!,
+                            false
+                        )
+                    }
+                    else -> {
+                        project.createTestConfigurationGenerationTask(
+                            androidTest.javaClass.getMethod(
+                                "getName"
+                            ).invoke(
+                                androidTest
+                            ) as String,
+                            androidTest.javaClass.getMethod(
+                                "getArtifacts"
+                            ).invoke(
+                                androidTest
+                            ) as Artifacts,
+                            defaultConfig.minSdk!!,
+                            defaultConfig.testInstrumentationRunner!!
+                        )
+                    }
+                }
+            }
+        )
+    } catch (cnfe: ClassNotFoundException) {
+        commonExtension.configureTestConfigGeneration(project)
+    }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
index 3e2d4a4..1afdfa8 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
@@ -452,16 +452,16 @@
         private val COBUILT_TEST_PATHS = setOf(
             // Install media tests together per b/128577735
             setOf(
-                ":support-media-test-client",
-                ":support-media-test-service",
-                ":support-media-test-client-previous",
-                ":support-media-test-service-previous"
+                ":media:version-compat-tests:client",
+                ":media:version-compat-tests:service",
+                ":media:version-compat-tests:client-previous",
+                ":media:version-compat-tests:service-previous"
             ),
             setOf(
-                ":support-media2-test-client",
-                ":support-media2-test-service",
-                ":support-media2-test-client-previous",
-                ":support-media2-test-service-previous"
+                ":media2:media2-session:version-compat-tests:client",
+                ":media2:media2-session:version-compat-tests:service",
+                ":media2:media2-session:version-compat-tests:client-previous",
+                ":media2:media2-session:version-compat-tests:service-previous"
             ), // Link graphics and material to always run @Large in presubmit per b/160624022
             setOf(
                 ":compose:ui:ui-graphics",
diff --git a/buildSrc/src/main/kotlin/androidx/build/studio/StudioPatcher.kt b/buildSrc/src/main/kotlin/androidx/build/studio/StudioPatcher.kt
index 976bf8d..5e51934 100644
--- a/buildSrc/src/main/kotlin/androidx/build/studio/StudioPatcher.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/studio/StudioPatcher.kt
@@ -31,15 +31,15 @@
 val NoopStudioPatcher: StudioPatcher = fun (_: StudioTask, _: Project, _: File) {}
 
 /**
- * Patch studio with the Kotlin 1.4.10 plugin, downloaded from Jetbrains
+ * Patch studio with the Performance Testing 202.6948.5 plugin, downloaded from Jetbrains
  */
-val KotlinStudioPatcher: StudioPatcher = fun (
+val PerformancePluginStudioPatcher: StudioPatcher = fun (
     studioTask: StudioTask,
     project: Project,
     studioInstallationDir: File
 ) {
-    val url = "https://plugins.jetbrains.com/plugin/download?rel=true&updateId=96650"
-    val tmpZip = File("${studioInstallationDir.absolutePath}/kotlinPlugin.zip")
+    val url = "https://plugins.jetbrains.com/plugin/download?rel=true&updateId=94393"
+    val tmpZip = File("${studioInstallationDir.absolutePath}/performanceTestingPlugin.zip")
         .absolutePath
 
     println("Downloading $url to $tmpZip")
@@ -52,7 +52,7 @@
 
     val platformUtilities = StudioPlatformUtilities.get(project.rootDir, studioInstallationDir)
     val pluginsDir = with(platformUtilities) { studioTask.pluginsDirectory }
-    File(pluginsDir, "Kotlin").deleteRecursively()
+    File(pluginsDir, "performanceTesting").deleteRecursively()
 
     project.exec { execSpec ->
         with(execSpec) {
diff --git a/buildSrc/src/main/kotlin/androidx/build/studio/StudioTask.kt b/buildSrc/src/main/kotlin/androidx/build/studio/StudioTask.kt
index 3315a32..049587e 100644
--- a/buildSrc/src/main/kotlin/androidx/build/studio/StudioTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/studio/StudioTask.kt
@@ -120,11 +120,6 @@
         File("$studioInstallationDir/STUDIOW_LICENSE_ACCEPTED")
     }
 
-    /**
-     * Allows for the patching of a Studio installation (including replacing plugins).
-     * TODO: Consider removing after Studio has switched to Kotlin 1.4
-     * b/162414740
-     */
     @get:Internal
     protected open val studioPatcher = NoopStudioPatcher
 
@@ -145,7 +140,7 @@
             // Finish install process
             successfulInstallFile.createNewFile()
         }
-        val successfulStudioPatch = File("$studioInstallationDir/PATCH_SUCCESSFUL")
+        val successfulStudioPatch = File("$studioInstallationDir/PLUGIN_PATCH_SUCCESSFUL")
         if (!successfulStudioPatch.exists()) {
             studioPatcher(this, project, studioInstallationDir)
             // Finish patch process
@@ -240,7 +235,7 @@
  */
 open class RootStudioTask : StudioTask() {
     override val studioArchiveCreator = UrlArchiveCreator
-    override val studioPatcher: StudioPatcher = KotlinStudioPatcher
+    override val studioPatcher: StudioPatcher = PerformancePluginStudioPatcher
     override val ideaProperties get() = projectRoot.resolve("development/studio/idea.properties")
 }
 
diff --git a/busytown/androidx_host_tests.sh b/busytown/androidx_host_tests.sh
index c753202..c1bbc97 100755
--- a/busytown/androidx_host_tests.sh
+++ b/busytown/androidx_host_tests.sh
@@ -10,6 +10,7 @@
     -Pandroidx.ignoreTestFailures \
     -Pandroidx.displayTestOutput=false \
     -Pandroidx.coverageEnabled=true \
+    -Pandroidx.validateNoUnrecognizedMessages \
     -Pandroidx.allWarningsAsErrors "$@"
 
 echo "Completing $0 at $(date)"
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt
index f9bcabe..28fe434 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt
@@ -85,6 +85,15 @@
 }
 
 /**
+ * A wrapper around [CaptureRequest]. This is useful in tests where we can't create an instance of
+ * [CaptureRequest] directly.
+ *
+ * TODO(codelogic, sushilnath): this wrapper can be removed if some of the methods in
+ * [Request.Listener] can be changed to not accept [CaptureRequest] directly.
+ */
+interface CaptureRequestWrapper : UnsafeWrapper<CaptureRequest>
+
+/**
  * RequestMetadata wraps together all of the information about specific CaptureRequest that was
  * submitted to Camera2.
  *
@@ -92,7 +101,7 @@
  * [CameraGraph]. This class will report the actual keys / values that were sent to camera2 (if
  * different) from the request that was used to create the Camera2 [CaptureRequest].
  */
-interface RequestMetadata : Metadata, UnsafeWrapper<CaptureRequest> {
+interface RequestMetadata : Metadata, CaptureRequestWrapper {
     operator fun <T> get(key: CaptureRequest.Key<T>): T?
     fun <T> getOrDefault(key: CaptureRequest.Key<T>, default: T): T
 
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Request.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Request.kt
index 5a109cd..dbfdf3b 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Request.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Request.kt
@@ -174,7 +174,7 @@
         fun onRequestSequenceCreated(
             request: Request,
             requestNumber: RequestNumber,
-            captureRequest: CaptureRequest,
+            captureRequest: CaptureRequestWrapper,
             streams: Map<StreamId, Surface>
         ) {
         }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/CameraGraphComponent.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/CameraGraphComponent.kt
index c95132b..640fc72 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/CameraGraphComponent.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/CameraGraphComponent.kt
@@ -97,12 +97,18 @@
         @Provides
         @ForCameraGraph
         fun provideRequestListeners(
-            graphConfig: CameraGraph.Config
+            graphConfig: CameraGraph.Config,
+            listener3A: Listener3A
         ): java.util.ArrayList<Request.Listener> {
             // TODO: Dagger doesn't appear to like standard kotlin lists. Replace this with a standard
             //   Kotlin list interfaces when dagger compiles with them.
-            // TODO: Add internal listeners before adding external global listeners.
-            return java.util.ArrayList(graphConfig.listeners)
+            val listeners = java.util.ArrayList<Request.Listener>()
+            listeners.add(listener3A)
+            // Listeners in CameraGraph.Config can de defined outside of the CameraPipe library,
+            // and since we iterate thought the listeners in order and invoke them, it appears
+            // beneficial to add the internal listeners first and then the graph config listeners.
+            listeners.addAll(graphConfig.listeners)
+            return listeners
         }
     }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/FrameMetadata.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/FrameMetadata.kt
index a65debf..b6a2236 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/FrameMetadata.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/FrameMetadata.kt
@@ -16,11 +16,13 @@
 
 package androidx.camera.camera2.pipe.impl
 
+import android.hardware.camera2.CaptureRequest
 import android.hardware.camera2.CaptureResult
 import android.hardware.camera2.TotalCaptureResult
 import android.os.Build
 import android.util.ArrayMap
 import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CaptureRequestWrapper
 import androidx.camera.camera2.pipe.FrameInfo
 import androidx.camera.camera2.pipe.FrameMetadata
 import androidx.camera.camera2.pipe.FrameNumber
@@ -131,4 +133,14 @@
         get() = result.frameNumber
 
     override fun unwrap(): TotalCaptureResult? = totalCaptureResult
+}
+
+/**
+ * Implementation of [CaptureRequestWrapper] that returns the underlying [CaptureRequest].
+ */
+@Suppress("SyntheticAccessor") // Using an inline class generates a synthetic constructor
+class AndroidCaptureRequest(private val captureRequest: CaptureRequest) : CaptureRequestWrapper {
+    override fun unwrap(): CaptureRequest {
+        return captureRequest
+    }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/Listener3A.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/Listener3A.kt
new file mode 100644
index 0000000..f1a0394
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/Listener3A.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.camera2.pipe.impl
+
+import android.view.Surface
+import androidx.camera.camera2.pipe.CaptureRequestWrapper
+import androidx.camera.camera2.pipe.FrameInfo
+import androidx.camera.camera2.pipe.FrameMetadata
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestMetadata
+import androidx.camera.camera2.pipe.RequestNumber
+import androidx.camera.camera2.pipe.StreamId
+import java.util.concurrent.CopyOnWriteArrayList
+import javax.inject.Inject
+
+/**
+ * A [Request.Listener] to receive partial and final metadata for each request sent to the camera
+ * device. It maintains a list of [Result3AStateListener] to which it broadcasts the updates and
+ * removes them as they are completed. This listener is useful for implementing 3A methods to
+ * look for desired 3A state changes.
+ */
+@CameraGraphScope
+class Listener3A @Inject constructor() : Request.Listener {
+    private val listeners: CopyOnWriteArrayList<Result3AStateListener> = CopyOnWriteArrayList()
+
+    override fun onRequestSequenceCreated(
+        request: Request,
+        requestNumber: RequestNumber,
+        captureRequest: CaptureRequestWrapper,
+        streams: Map<StreamId, Surface>
+    ) {
+        var listenersCopy: List<Result3AStateListener>
+        synchronized(this) {
+            listenersCopy = listeners.toList()
+        }
+        for (listener in listenersCopy) {
+            listener.onRequestSequenceCreated(requestNumber)
+        }
+    }
+
+    override fun onPartialCaptureResult(
+        requestMetadata: RequestMetadata,
+        frameNumber: FrameNumber,
+        captureResult: FrameMetadata
+    ) {
+        updateListeners(requestMetadata.requestNumber, captureResult)
+    }
+
+    override fun onTotalCaptureResult(
+        requestMetadata: RequestMetadata,
+        frameNumber: FrameNumber,
+        totalCaptureResult: FrameInfo
+    ) {
+        updateListeners(requestMetadata.requestNumber, totalCaptureResult.metadata)
+    }
+
+    fun addListener(listener: Result3AStateListener) {
+        synchronized(this) {
+            listeners.add(listener)
+        }
+    }
+
+    private fun updateListeners(requestNumber: RequestNumber, metadata: FrameMetadata) {
+        for (listener in listeners) {
+            if (listener.update(requestNumber, metadata)) {
+                listeners.remove(listener)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/RequestProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/RequestProcessor.kt
index 573d89c..f8fbcc3 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/RequestProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/RequestProcessor.kt
@@ -627,7 +627,7 @@
             listener.onRequestSequenceCreated(
                 request.request,
                 request.requestNumber,
-                captureRequests[index],
+                AndroidCaptureRequest(captureRequests[index]),
                 streamMap
             )
         }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/Result3AStateListener.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/Result3AStateListener.kt
index 60f1c45..56182d6 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/Result3AStateListener.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/impl/Result3AStateListener.kt
@@ -34,11 +34,16 @@
  * This update method can be called multiple times as we get newer [CaptureResult]s from the camera
  * device. This class also exposes a [Deferred] to query the status of desired state.
  */
-class Result3AStateListener(
+interface Result3AStateListener {
+    fun onRequestSequenceCreated(requestNumber: RequestNumber)
+    fun update(requestNumber: RequestNumber, frameMetadata: FrameMetadata): Boolean
+}
+
+class Result3AStateListenerImpl (
     private val exitConditionForKeys: Map<CaptureResult.Key<*>, List<Any>>,
     private val frameLimit: Int? = null,
     private val timeLimitNs: Long? = null
-) {
+) : Result3AStateListener {
 
     init {
         require(exitConditionForKeys.isNotEmpty()) { "Exit condition map for keys is empty." }
@@ -51,7 +56,7 @@
     @GuardedBy("this")
     private var initialRequestNumber: RequestNumber? = null
 
-    fun onRequestSequenceCreated(requestNumber: RequestNumber) {
+    override fun onRequestSequenceCreated(requestNumber: RequestNumber) {
         synchronized(this) {
             if (initialRequestNumber == null) {
                 initialRequestNumber = requestNumber
@@ -59,7 +64,7 @@
         }
     }
 
-    fun update(requestNumber: RequestNumber, frameMetadata: FrameMetadata): Boolean {
+    override fun update(requestNumber: RequestNumber, frameMetadata: FrameMetadata): Boolean {
         // Save some compute if the task is already complete or has been canceled.
         if (deferred.isCompleted || deferred.isCancelled) {
             return true
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Listener3ATest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Listener3ATest.kt
new file mode 100644
index 0000000..051dd1e
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Listener3ATest.kt
@@ -0,0 +1,175 @@
+/*
+ * 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.camera2.pipe.impl
+
+import android.hardware.camera2.CaptureResult
+import android.os.Build
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestNumber
+import androidx.camera.camera2.pipe.testing.CameraPipeRobolectricTestRunner
+import androidx.camera.camera2.pipe.testing.FakeCaptureRequestWrapper
+import androidx.camera.camera2.pipe.testing.FakeFrameMetadata
+import androidx.camera.camera2.pipe.testing.FakeRequestMetadata
+import androidx.camera.camera2.pipe.testing.UpdateCounting3AStateListener
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(CameraPipeRobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class Listener3ATest {
+    @Test
+    fun testListenersInvoked() {
+        val result3AStateListener = Result3AStateListenerImpl(
+            mapOf(
+                CaptureResult.CONTROL_AF_MODE to listOf(CaptureResult.CONTROL_AF_MODE_AUTO)
+            )
+        )
+        val listener3A = Listener3A()
+        listener3A.addListener(result3AStateListener)
+
+        // The deferred result of 3a state listener shouldn't be complete right now.
+        assertThat(result3AStateListener.getDeferredResult().isCompleted).isFalse()
+        listener3A.onRequestSequenceCreated(
+            Request(listOf()),
+            RequestNumber(1),
+            FakeCaptureRequestWrapper(),
+            mapOf()
+        )
+
+        val requestMetadata = FakeRequestMetadata(requestNumber = RequestNumber(1))
+        val frameNumber = FrameNumber(1L)
+        val captureResult = FakeFrameMetadata(
+            mapOf(
+                CaptureResult.CONTROL_AF_MODE to CaptureResult.CONTROL_AF_MODE_AUTO
+            )
+        )
+        // Once the correct metadata is updated the listener3A should broadcast it to the
+        // result3AState listener added to it, making the deferred result complete.
+        listener3A.onPartialCaptureResult(requestMetadata, frameNumber, captureResult)
+        assertThat(result3AStateListener.getDeferredResult().isCompleted).isTrue()
+    }
+
+    @Test
+    fun testListenersInvokedWithMultipleUpdates() {
+        val result3AStateListener = UpdateCounting3AStateListener(
+            Result3AStateListenerImpl(
+                mapOf(
+                    CaptureResult.CONTROL_AF_MODE to listOf(CaptureResult.CONTROL_AF_MODE_AUTO)
+                )
+            )
+        )
+        val listener3A = Listener3A()
+        listener3A.addListener(result3AStateListener)
+
+        listener3A.onRequestSequenceCreated(
+            Request(listOf()),
+            RequestNumber(1),
+            FakeCaptureRequestWrapper(),
+            mapOf()
+        )
+
+        val requestMetadata = FakeRequestMetadata(requestNumber = RequestNumber(1))
+        val captureResult = FakeFrameMetadata(
+            mapOf(
+                CaptureResult.CONTROL_AF_MODE to CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE
+            )
+        )
+        val captureResult1 = FakeFrameMetadata(
+            mapOf(
+                CaptureResult.CONTROL_AF_MODE to CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE
+            )
+        )
+        listener3A.onPartialCaptureResult(requestMetadata, FrameNumber(1), captureResult)
+        assertThat(result3AStateListener.updateCount).isEqualTo(1)
+
+        // Since the first update didn't have the right key and it's desired value, the second
+        // update should also be supplies to the result3AListener.
+        listener3A.onPartialCaptureResult(requestMetadata, FrameNumber(2), captureResult1)
+        assertThat(result3AStateListener.updateCount).isEqualTo(2)
+    }
+
+    @Test
+    fun testListenersAreRemovedWhenDone() {
+        val result3AStateListener1 = UpdateCounting3AStateListener(
+            Result3AStateListenerImpl(
+                mapOf(
+                    CaptureResult.CONTROL_AF_MODE to listOf(CaptureResult.CONTROL_AF_MODE_AUTO)
+                )
+            )
+        )
+        val result3AStateListener2 = UpdateCounting3AStateListener(
+            Result3AStateListenerImpl(
+                mapOf(
+                    CaptureResult.CONTROL_AE_MODE to listOf(CaptureResult.CONTROL_AE_MODE_OFF)
+                )
+            )
+        )
+
+        val listener3A = Listener3A()
+        listener3A.addListener(result3AStateListener1)
+        listener3A.addListener(result3AStateListener2)
+
+        val requestMetadata = FakeRequestMetadata(requestNumber = RequestNumber(1))
+        val frameNumber = FrameNumber(1L)
+        val captureResult = FakeFrameMetadata(
+            mapOf(
+                CaptureResult.CONTROL_AF_MODE to CaptureResult.CONTROL_AF_MODE_AUTO
+            )
+        )
+
+        // There should be no update to either of the listeners right now.
+        assertThat(result3AStateListener1.updateCount).isEqualTo(0)
+        assertThat(result3AStateListener2.updateCount).isEqualTo(0)
+
+        listener3A.onRequestSequenceCreated(
+            Request(listOf()),
+            RequestNumber(1),
+            FakeCaptureRequestWrapper(),
+            mapOf()
+        )
+
+        // Once the metadata for correct AF mode is updated, the listener3A should broadcast it to
+        // the result3AState listeners added to it, making result3AStateListener1 complete.
+        listener3A.onPartialCaptureResult(requestMetadata, frameNumber, captureResult)
+        assertThat(result3AStateListener1.updateCount).isEqualTo(1)
+        assertThat(result3AStateListener2.updateCount).isEqualTo(1)
+
+        // Once the metadata for correct AE mode is updated, the listener3A should broadcast it to
+        // the result3AState listeners added to it, making result3AStateListener2 complete. Since
+        // result3AStateListener1 was already completed it will not be updated again.
+        val captureResult1 = FakeFrameMetadata(
+            mapOf(
+                CaptureResult.CONTROL_AF_MODE to CaptureResult.CONTROL_AF_MODE_AUTO,
+                CaptureResult.CONTROL_AE_MODE to CaptureResult.CONTROL_AE_MODE_OFF
+            )
+        )
+        listener3A.onPartialCaptureResult(requestMetadata, frameNumber, captureResult1)
+        assertThat(result3AStateListener1.updateCount).isEqualTo(1)
+        assertThat(result3AStateListener2.updateCount).isEqualTo(2)
+
+        // Since both result3AStateListener1 and result3AStateListener2 are complete, they will not
+        // receive further updates.
+        listener3A.onPartialCaptureResult(requestMetadata, frameNumber, captureResult1)
+        assertThat(result3AStateListener1.updateCount).isEqualTo(1)
+        assertThat(result3AStateListener2.updateCount).isEqualTo(2)
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Result3AStateListenerTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Result3AStateListenerImplTest.kt
similarity index 95%
rename from camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Result3AStateListenerTest.kt
rename to camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Result3AStateListenerImplTest.kt
index fac4250..5013b65 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Result3AStateListenerTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/impl/Result3AStateListenerImplTest.kt
@@ -32,10 +32,10 @@
 @RunWith(CameraPipeRobolectricTestRunner::class)
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 @OptIn(ExperimentalCoroutinesApi::class)
-class Result3AStateListenerTest {
+class Result3AStateListenerImplTest {
     @Test
     fun testWithNoUpdate() {
-        val listenerForKeys = Result3AStateListener(
+        val listenerForKeys = Result3AStateListenerImpl(
             mapOf(
                 CaptureResult.CONTROL_AF_STATE to
                     listOf(
@@ -48,7 +48,7 @@
 
     @Test
     fun testKeyWithUndesirableValueInFrameMetadata() {
-        val listenerForKeys = Result3AStateListener(
+        val listenerForKeys = Result3AStateListenerImpl(
             mapOf(
                 CaptureResult.CONTROL_AF_STATE to
                     listOf(
@@ -70,7 +70,7 @@
 
     @Test
     fun testKeyWithDesirableValueInFrameMetadata() {
-        val listenerForKeys = Result3AStateListener(
+        val listenerForKeys = Result3AStateListenerImpl(
             mapOf(
                 CaptureResult.CONTROL_AF_STATE to
                     listOf(
@@ -91,7 +91,7 @@
 
     @Test
     fun testKeyNotPresentInFrameMetadata() {
-        val listenerForKeys = Result3AStateListener(
+        val listenerForKeys = Result3AStateListenerImpl(
             mapOf(
                 CaptureResult.CONTROL_AF_STATE to
                     listOf(
@@ -112,7 +112,7 @@
 
     @Test
     fun testMultipleKeysWithDesiredValuesInFrameMetadata() {
-        val listenerForKeys = Result3AStateListener(
+        val listenerForKeys = Result3AStateListenerImpl(
             mapOf(
                 CaptureResult.CONTROL_AF_STATE to
                     listOf(
@@ -136,7 +136,7 @@
 
     @Test
     fun testMultipleKeysWithDesiredValuesInFrameMetadataForASubset() {
-        val listenerForKeys = Result3AStateListener(
+        val listenerForKeys = Result3AStateListenerImpl(
             mapOf(
                 CaptureResult.CONTROL_AF_STATE to
                     listOf(
@@ -159,7 +159,7 @@
 
     @Test
     fun testMultipleUpdates() {
-        val listenerForKeys = Result3AStateListener(
+        val listenerForKeys = Result3AStateListenerImpl(
             mapOf(
                 CaptureResult.CONTROL_AF_STATE to
                     listOf(
@@ -191,7 +191,7 @@
 
     @Test
     fun testTimeLimit() {
-        val listenerForKeys = Result3AStateListener(
+        val listenerForKeys = Result3AStateListenerImpl(
             exitConditionForKeys = mapOf(
                 CaptureResult.CONTROL_AF_STATE to
                     listOf(CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED)
@@ -232,7 +232,7 @@
 
     @Test
     fun testFrameLimit() {
-        val listenerForKeys = Result3AStateListener(
+        val listenerForKeys = Result3AStateListenerImpl(
             exitConditionForKeys = mapOf(
                 CaptureResult.CONTROL_AF_STATE to
                     listOf(CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED)
@@ -289,7 +289,7 @@
 
     @Test
     fun testIgnoreUpdatesFromEarlierRequests() {
-        val listenerForKeys = Result3AStateListener(
+        val listenerForKeys = Result3AStateListenerImpl(
             mapOf(
                 CaptureResult.CONTROL_AF_STATE to
                     listOf(
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeMetadata.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeMetadata.kt
index 53c8525..1d96974 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeMetadata.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeMetadata.kt
@@ -26,6 +26,7 @@
 import android.view.Surface
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraMetadata
+import androidx.camera.camera2.pipe.CaptureRequestWrapper
 import androidx.camera.camera2.pipe.FrameInfo
 import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.Metadata
@@ -156,3 +157,14 @@
         )
     }
 }
+
+/**
+ * A fake [CaptureRequestWrapper].
+ */
+class FakeCaptureRequestWrapper : CaptureRequestWrapper {
+    override fun unwrap(): CaptureRequest? {
+        throw UnsupportedOperationException(
+            "FakeCaptureRequestWrapper does not wrap a real CaptureRequest object"
+        )
+    }
+}
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/UpdateCounting3AStateListener.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/UpdateCounting3AStateListener.kt
new file mode 100644
index 0000000..9351405
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/UpdateCounting3AStateListener.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.camera2.pipe.testing
+
+import androidx.camera.camera2.pipe.FrameMetadata
+import androidx.camera.camera2.pipe.RequestNumber
+import androidx.camera.camera2.pipe.impl.Result3AStateListener
+
+/**
+ * Wrapper on Result3AStateListenerImpl to keep track of the number of times the update method is
+ * called.
+ */
+class UpdateCounting3AStateListener(
+    private val listener: Result3AStateListener
+) : Result3AStateListener {
+    var updateCount = 0
+
+    override fun onRequestSequenceCreated(requestNumber: RequestNumber) {
+        listener.onRequestSequenceCreated(requestNumber)
+    }
+
+    override fun update(requestNumber: RequestNumber, frameMetadata: FrameMetadata): Boolean {
+        updateCount++
+        return listener.update(requestNumber, frameMetadata)
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
index 5d75838..c076f83 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
@@ -49,6 +49,7 @@
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.internal.CameraUseCaseAdapter;
 import androidx.camera.testing.CameraUtil;
+import androidx.camera.testing.GLUtil;
 import androidx.camera.testing.SurfaceTextureProvider;
 import androidx.core.util.Consumer;
 import androidx.test.core.app.ApplicationProvider;
@@ -492,6 +493,62 @@
         assertThat(preview.getTargetRotation()).isEqualTo(Surface.ROTATION_180);
     }
 
+    @Test
+    public void setNullSurfaceProvider_shouldStopPreview() throws InterruptedException {
+        // Arrange.
+        final Preview preview = new Preview.Builder().build();
+
+        // Act.
+        // TODO(b/160261462) move off of main thread when setSurfaceProvider does not need to be
+        //  done on the main thread
+        mInstrumentation.runOnMainSync(() ->
+                preview.setSurfaceProvider(CameraXExecutors.mainThreadExecutor(),
+                        createSurfaceTextureProvider(
+                                new SurfaceTextureProvider.SurfaceTextureCallback() {
+                                    private boolean mIsReleased = false;
+                                    @Override
+                                    public void onSurfaceTextureReady(
+                                            @NonNull SurfaceTexture surfaceTexture,
+                                            @NonNull Size resolution) {
+                                        surfaceTexture.attachToGLContext(
+                                                GLUtil.getTexIdFromGLContext());
+                                        surfaceTexture.setOnFrameAvailableListener(st -> {
+                                            mSurfaceFutureSemaphore.release();
+                                            synchronized (this) {
+                                                if (!mIsReleased) {
+                                                    surfaceTexture.updateTexImage();
+                                                }
+                                            }
+                                        });
+                                    }
+
+                                    @Override
+                                    public void onSafeToRelease(
+                                            @NonNull SurfaceTexture surfaceTexture) {
+                                        synchronized (this) {
+                                            mIsReleased = true;
+                                            surfaceTexture.release();
+                                        }
+                                    }
+                                })));
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
+
+        // Assert.
+        // Wait until preview gets frame.
+        assertThat(mSurfaceFutureSemaphore.tryAcquire(10, TimeUnit.SECONDS)).isTrue();
+
+        // Act.
+        // TODO(b/160261462) move off of main thread when setSurfaceProvider does not need to be
+        //  done on the main thread
+        mInstrumentation.runOnMainSync(() ->
+                preview.setSurfaceProvider(CameraXExecutors.mainThreadExecutor(), null)
+        );
+
+        // Assert.
+        // No frame coming for 3 seconds in 10 seconds timeout.
+        assertThat(noFrameCome(3000L, 10000L)).isTrue();
+    }
+
     private Executor getWorkExecutorWithNamedThread() {
         final ThreadFactory threadFactory = runnable -> new Thread(runnable, ANY_THREAD_NAME);
         return Executors.newSingleThreadExecutor(threadFactory);
@@ -517,4 +574,39 @@
             }
         });
     }
+
+    /*
+     * Check if there is no frame callback for `noFrameIntervalMs` milliseconds, then it will
+     * return true; If the total check time is over `timeoutMs` milliseconds, then it will return
+     * false.
+     */
+    private boolean noFrameCome(long noFrameIntervalMs, long timeoutMs)
+            throws InterruptedException {
+        if (noFrameIntervalMs <= 0 || timeoutMs <= 0) {
+            throw new IllegalArgumentException("Time can't be negative value.");
+        }
+        if (timeoutMs < noFrameIntervalMs) {
+            throw new IllegalArgumentException(
+                    "timeoutMs should be larger than noFrameIntervalMs.");
+        }
+        final long checkFrequency = 200L;
+        long totalCheckTime = 0L;
+        long zeroFrameTimer = 0L;
+        do {
+            Thread.sleep(checkFrequency);
+            if (mSurfaceFutureSemaphore.availablePermits() > 0) {
+                // Has frame, reset timer and frame count.
+                zeroFrameTimer = 0;
+                mSurfaceFutureSemaphore.drainPermits();
+            } else {
+                zeroFrameTimer += checkFrequency;
+            }
+            if (zeroFrameTimer > noFrameIntervalMs) {
+                return true;
+            }
+            totalCheckTime += checkFrequency;
+        } while (totalCheckTime < timeoutMs);
+
+        return false;
+    }
 }
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
index 26ffbd2..8a00e17 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
@@ -23,6 +23,7 @@
 
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -1085,6 +1086,52 @@
         assertThat(captureSession.getState()).isEqualTo(State.RELEASED);
     }
 
+    @Test
+    public void setSessionConfigWithoutSurface_shouldStopRepeating()
+            throws ExecutionException, InterruptedException {
+        // Create Surface
+        ImageReader imageReader =
+                ImageReader.newInstance(640, 480, ImageFormat.YUV_420_888, /*maxImages*/ 2);
+        DeferrableSurface surface = new ImmediateSurface(imageReader.getSurface());
+
+        // Prepare SessionConfig builder
+        SessionConfig.Builder builder = new SessionConfig.Builder();
+        builder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW);
+        CameraCaptureSession.StateCallback stateCallback =
+                Mockito.mock(CameraCaptureSession.StateCallback.class);
+        builder.addSessionStateCallback(stateCallback);
+        CameraCaptureCallback captureCallback =
+                Mockito.mock(CameraCaptureCallback.class);
+        builder.addRepeatingCameraCaptureCallback(captureCallback);
+
+        // Create SessionConfig without Surface
+        SessionConfig sessionConfigWithoutSurface = builder.build();
+
+        // Create SessionConfig with Surface
+        builder.addSurface(surface);
+        SessionConfig sessionConfigWithSurface = builder.build();
+
+        // Open CaptureSession
+        CaptureSession captureSession = createCaptureSession();
+        captureSession.open(sessionConfigWithSurface, mCameraDeviceHolder.get(),
+                mCaptureSessionOpenerBuilder.build()).get();
+
+        // Activate repeating request
+        captureSession.setSessionConfig(sessionConfigWithSurface);
+        verify(captureCallback, timeout(3000L).atLeast(3)).onCaptureCompleted(any());
+
+        // Deactivate repeating request
+        clearInvocations(stateCallback);
+        captureSession.setSessionConfig(sessionConfigWithoutSurface);
+
+        // Wait for #onReady which means there is no repeating request.
+        verify(stateCallback, timeout(3000L)).onReady(any());
+
+        // Clean up
+        surface.close();
+        surface.getTerminationFuture().addListener(() -> imageReader.close(),
+                CameraXExecutors.directExecutor());
+    }
 
     /**
      * A implementation to test {@link CameraEventCallback} on CaptureSession.
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
index 5885d6b..7418b99 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
@@ -553,7 +553,7 @@
     /**
      * Sets the {@link CaptureRequest} so that the camera will start producing data.
      *
-     * <p>Will skip setting requests if there are no surfaces since it is illegal to do so.
+     * <p>It will stop running repeating if there are no surfaces.
      */
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     @GuardedBy("mStateLock")
@@ -566,6 +566,15 @@
         CaptureConfig captureConfig = mSessionConfig.getRepeatingCaptureConfig();
         if (captureConfig.getSurfaces().isEmpty()) {
             Logger.d(TAG, "Skipping issueRepeatingCaptureRequests for no surface.");
+            try {
+                // At least from Android L, framework will ignore the stopRepeating() if
+                // there is no ongoing repeating request, so it should be safe to always call
+                // stopRepeating() without checking if there is a repeating request.
+                mSynchronizedCaptureSession.stopRepeating();
+            } catch (CameraAccessException e) {
+                Logger.e(TAG, "Unable to access camera: " + e.getMessage());
+                Thread.dumpStack();
+            }
             return;
         }
 
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/preview/transform/PreviewTransformTest.java b/camera/camera-view/src/androidTest/java/androidx/camera/view/preview/transform/PreviewTransformTest.java
deleted file mode 100644
index f3c94c7..0000000
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/preview/transform/PreviewTransformTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.util.Size;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import androidx.camera.testing.fakes.FakeActivity;
-import androidx.camera.view.PreviewView;
-import androidx.core.content.ContextCompat;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.rule.ActivityTestRule;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class PreviewTransformTest {
-
-    private static final Size CONTAINER = new Size(800, 450);
-    private static final Size BUFFER = new Size(400, 300);
-
-    @Rule
-    public final ActivityTestRule<FakeActivity> mActivityTestRule = new ActivityTestRule<>(
-            FakeActivity.class);
-
-    private FrameLayout mContainer;
-    private View mView;
-
-    @Before
-    public void setUp() throws Throwable {
-        mActivityTestRule.runOnUiThread(() -> {
-            final Context context = mActivityTestRule.getActivity();
-
-            final FrameLayout root = new FrameLayout(context);
-
-            mContainer = new FrameLayout(context);
-            mContainer.setLayoutParams(
-                    new FrameLayout.LayoutParams(CONTAINER.getWidth(), CONTAINER.getHeight()));
-
-            mView = new View(context);
-            mView.setLayoutParams(
-                    new FrameLayout.LayoutParams(BUFFER.getWidth(), BUFFER.getHeight()));
-            mView.setBackgroundColor(
-                    ContextCompat.getColor(context, android.R.color.holo_blue_dark));
-
-            mContainer.addView(mView);
-            root.addView(mContainer);
-            mActivityTestRule.getActivity().setContentView(root);
-        });
-    }
-
-    @Test
-    @UiThreadTest
-    public void fillStart() {
-        final PreviewTransform previewTransform = new PreviewTransform();
-        previewTransform.setScaleType(PreviewView.ScaleType.FILL_START);
-        previewTransform.applyCurrentScaleType(mContainer, mView, BUFFER);
-
-        // Assert the preview fills its container
-        assertThat(mView.getWidth() * mView.getScaleX()).isAtLeast(mContainer.getWidth());
-        assertThat(mView.getHeight() * mView.getScaleY()).isAtLeast(mContainer.getHeight());
-    }
-
-    @Test
-    @UiThreadTest
-    public void fillCenter() {
-        final PreviewTransform previewTransform = new PreviewTransform();
-        previewTransform.setScaleType(PreviewView.ScaleType.FILL_CENTER);
-        previewTransform.applyCurrentScaleType(mContainer, mView, BUFFER);
-
-        // Assert the preview fills its container
-        assertThat(mView.getWidth() * mView.getScaleX()).isAtLeast(mContainer.getWidth());
-        assertThat(mView.getHeight() * mView.getScaleY()).isAtLeast(mContainer.getHeight());
-    }
-
-    @Test
-    @UiThreadTest
-    public void fillEnd() {
-        final PreviewTransform previewTransform = new PreviewTransform();
-        previewTransform.setScaleType(PreviewView.ScaleType.FILL_END);
-        previewTransform.applyCurrentScaleType(mContainer, mView, BUFFER);
-
-        // Assert the preview fills its container
-        assertThat(mView.getWidth() * mView.getScaleX()).isAtLeast(mContainer.getWidth());
-        assertThat(mView.getHeight() * mView.getScaleY()).isAtLeast(mContainer.getHeight());
-    }
-}
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/preview/transform/transformation/TransformationDeviceTest.java b/camera/camera-view/src/androidTest/java/androidx/camera/view/preview/transform/transformation/TransformationDeviceTest.java
deleted file mode 100644
index df86666..0000000
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/preview/transform/transformation/TransformationDeviceTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform.transformation;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Activity;
-import android.view.View;
-
-import androidx.camera.testing.fakes.FakeActivity;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.rule.ActivityTestRule;
-
-import org.junit.Rule;
-import org.junit.Test;
-
-public class TransformationDeviceTest {
-
-    @Rule
-    public final ActivityTestRule<FakeActivity> mRule = new ActivityTestRule<>(FakeActivity.class);
-
-    @Test
-    @UiThreadTest
-    public void getTransformationFromView() {
-        final Activity activity = mRule.getActivity();
-        final View view = new View(activity);
-        view.setScaleX(2F);
-        view.setScaleY(0.5F);
-        view.setTranslationX(100);
-        view.setTranslationY(35.5F);
-        view.setRotation(3.14F);
-        activity.setContentView(view);
-
-        final Transformation transformation = Transformation.getTransformation(view);
-
-        assertThat(transformation.getScaleX()).isEqualTo(2F);
-        assertThat(transformation.getScaleY()).isEqualTo(0.5F);
-        assertThat(transformation.getTransX()).isEqualTo(100);
-        assertThat(transformation.getTransY()).isEqualTo(35.5F);
-        assertThat(transformation.getRotation()).isEqualTo(3.14F);
-    }
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/PreviewCorrector.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/PreviewCorrector.java
deleted file mode 100644
index 2d7418a..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/PreviewCorrector.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform;
-
-import android.util.Pair;
-import android.util.Size;
-import android.view.Surface;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.camera.view.preview.transform.transformation.PreviewCorrectionTransformation;
-
-/** Corrects a camera preview by scaling and/or rotating it so that it matches the display. */
-final class PreviewCorrector {
-
-    private PreviewCorrector() {
-    }
-
-    /**
-     * Corrects a camera preview by scaling and rotating it.
-     *
-     * @param container                 Preview container
-     * @param preview                   Preview view (a {@link android.view.TextureView} or
-     *                                  {@link android.view.SurfaceView})
-     * @param bufferSize                Camera output size
-     * @param sensorDimensionFlipNeeded True if the sensor x and y dimensions need to be flipped.
-     * @param deviceRotation            If the app is not running in remote display mode, set the
-     *                                  parameter as {@link RotationTransform#ROTATION_AUTOMATIC}.
-     *                                  Then, the rotation value queried from the preview will be
-     *                                  used to do the transformation calculations. If the app is
-     *                                  running in remote display mode, the device rotation value
-     *                                  needs to be provided to make the result be rotated into
-     *                                  correct orientation. The device rotation should be obtained
-     *                                  from {@link android.view.OrientationEventListener} and
-     *                                  needs to be converted into {@link Surface#ROTATION_0},
-     *                                  {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180}
-     *                                  , or {@link Surface#ROTATION_270}.
-     */
-    @NonNull
-    static PreviewCorrectionTransformation getCorrectionTransformation(
-            @NonNull final View container, @NonNull final View preview,
-            @NonNull final Size bufferSize, final boolean sensorDimensionFlipNeeded,
-            final int deviceRotation) {
-        final int rotation = (int) RotationTransform.getRotationDegrees(preview, deviceRotation);
-        final Pair<Float, Float> scaleXY = getCorrectionScale(container, preview,
-                bufferSize, sensorDimensionFlipNeeded);
-        return new PreviewCorrectionTransformation(scaleXY.first, scaleXY.second, -rotation);
-    }
-
-    /**
-     * Computes the scales on both the x and y axes so that the preview can be corrected.
-     *
-     * @param container                 Preview container
-     * @param preview                   Preview view (a {@link android.view.TextureView} or
-     *                                  {@link android.view.SurfaceView})
-     * @param bufferSize                Camera output size
-     * @param sensorDimensionFlipNeeded True if the sensor x and y dimensions need to be flipped.
-     * @return The scales on both the x and y axes so that the preview can be corrected.
-     */
-    private static Pair<Float, Float> getCorrectionScale(@NonNull final View container,
-            @NonNull final View preview, @NonNull final Size bufferSize,
-            final boolean sensorDimensionFlipNeeded) {
-
-        // Scaling only makes sense when none of the dimensions are equal to zero. In the
-        // opposite case, a default scale of 1 is returned,
-        if (container.getWidth() == 0 || container.getHeight() == 0 || preview.getWidth() == 0
-                || preview.getHeight() == 0 || bufferSize.getWidth() == 0
-                || bufferSize.getHeight() == 0) {
-            return new Pair<>(1F, 1F);
-        }
-
-        final int bufferWidth;
-        final int bufferHeight;
-        if (sensorDimensionFlipNeeded) {
-            bufferWidth = bufferSize.getHeight();
-            bufferHeight = bufferSize.getWidth();
-        } else {
-            bufferWidth = bufferSize.getWidth();
-            bufferHeight = bufferSize.getHeight();
-        }
-
-        // Scale the buffers back to the original output size.
-        float scaleX = bufferWidth / (float) preview.getWidth();
-        float scaleY = bufferHeight / (float) preview.getHeight();
-        return new Pair<>(scaleX, scaleY);
-    }
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/PreviewTransform.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/PreviewTransform.java
deleted file mode 100644
index aedad72..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/PreviewTransform.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform;
-
-import static androidx.camera.view.preview.transform.transformation.Transformation.getTransformation;
-
-import android.util.Size;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.camera.core.impl.ImageOutputConfig;
-import androidx.camera.view.PreviewView;
-import androidx.camera.view.preview.transform.transformation.Transformation;
-
-/**
- * Transforms the camera preview using a supported {@link PreviewView.ScaleType}.
- * <p>
- * Holds a reference to the {@link PreviewView.ScaleType} that's being applied to the preview.
- * This attribute is used by both {@link PreviewView} (see {@link PreviewView#getScaleType()} and
- * {@link PreviewView#setScaleType(PreviewView.ScaleType)}) and its implementation (when
- * correcting the preview and updating the {@link PreviewView.ScaleType}).
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public final class PreviewTransform {
-
-    private static final PreviewView.ScaleType DEFAULT_SCALE_TYPE =
-            PreviewView.ScaleType.FILL_CENTER;
-
-    @NonNull
-    private PreviewView.ScaleType mScaleType = DEFAULT_SCALE_TYPE;
-
-    @Nullable
-    private Transformation mCurrentTransformation;
-
-    private boolean mSensorDimensionFlipNeeded = true;
-
-    private int mDeviceRotation = RotationTransform.ROTATION_AUTOMATIC;
-
-    /** Returns the {@link PreviewView.ScaleType} currently applied to the preview. */
-    @NonNull
-    public PreviewView.ScaleType getScaleType() {
-        return mScaleType;
-    }
-
-    /** Sets the {@link PreviewView.ScaleType} to be applied to the preview. */
-    public void setScaleType(@NonNull PreviewView.ScaleType scaleType) {
-        mScaleType = scaleType;
-    }
-
-    /**
-     * Returns the current {@link Transformation} applied to the preview, or {@code null} if the
-     * preview hasn't started yet.
-     */
-    @Nullable
-    public Transformation getCurrentTransformation() {
-        return mCurrentTransformation;
-    }
-
-    /** Returns whether the device sensor x and y dimensions need to be flipped. */
-    public boolean isSensorDimensionFlipNeeded() {
-        return mSensorDimensionFlipNeeded;
-    }
-
-    /**
-     * Sets whether the target device sensor dimensions need to be flipped when calculating the
-     * transform.
-     */
-    public void setSensorDimensionFlipNeeded(boolean sensorDimensionFlipNeeded) {
-        mSensorDimensionFlipNeeded = sensorDimensionFlipNeeded;
-    }
-
-    /** Returns current device rotation value. */
-    public int getDeviceRotation() {
-        return mDeviceRotation;
-    }
-
-    /**
-     * Sets the device rotation value that will affect the transform calculations.
-     */
-    public void setDeviceRotation(@ImageOutputConfig.RotationValue int deviceRotation) {
-        mDeviceRotation = deviceRotation;
-    }
-
-    /** Applies the current {@link PreviewView.ScaleType} on the passed in preview. */
-    public void applyCurrentScaleType(@NonNull final View container, @NonNull final View view,
-            @NonNull final Size bufferSize) {
-        resetPreview(view);
-        correctPreview(container, view, bufferSize);
-        applyScaleTypeInternal(container, view, mScaleType, mDeviceRotation);
-    }
-
-    private void resetPreview(@NonNull View view) {
-        final Transformation reset = new Transformation();
-        applyTransformation(view, reset);
-    }
-
-    /** Corrects the preview. */
-    private void correctPreview(@NonNull final View container, @NonNull final View view,
-            @NonNull final Size bufferSize) {
-        final Transformation correct = PreviewCorrector.getCorrectionTransformation(container, view,
-                bufferSize, mSensorDimensionFlipNeeded, mDeviceRotation);
-        applyTransformation(view, correct);
-    }
-
-    /** Applies the specified {@link PreviewView.ScaleType} on top of the corrected preview. */
-    private void applyScaleTypeInternal(@NonNull final View container,
-            @NonNull final View view, @NonNull final PreviewView.ScaleType scaleType,
-            final int deviceRotation) {
-        final Transformation current = getTransformation(view);
-        final Transformation transformation = ScaleTypeTransform.getTransformation(container, view,
-                scaleType, deviceRotation);
-        applyTransformation(view, current.add(transformation));
-    }
-
-    /**
-     * Applies a {@link Transformation} on the passed in preview while overriding any previous
-     * preview {@linkplain Transformation transformations}
-     */
-    private void applyTransformation(@NonNull final View view,
-            @NonNull final Transformation transformation) {
-        view.setX(0);
-        view.setY(0);
-        view.setScaleX(transformation.getScaleX());
-        view.setScaleY(transformation.getScaleY());
-        view.setTranslationX(transformation.getTransX());
-        view.setTranslationY(transformation.getTransY());
-        view.setRotation(transformation.getRotation());
-
-        mCurrentTransformation = transformation;
-    }
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/RotationTransform.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/RotationTransform.java
deleted file mode 100644
index 624c056..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/RotationTransform.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform;
-
-import android.view.Display;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-
-final class RotationTransform {
-    // Queries rotation value from view automatically.
-    static final int ROTATION_AUTOMATIC = -1;
-
-    private RotationTransform() {
-    }
-
-    /**
-     * Computes the rotation in degrees from its natural orientation.
-     *
-     * <p>When a application is running in remote display, the correct rotation degrees should
-     * be obtained from the device rotation. The rotation from the {@link View} may cause incorrect
-     * transform calculation results.
-     *
-     * @param view The {@link View} used to show the preview.
-     * @param deviceRotation The device rotation value provided by the developers.
-     * @return The rotation in degrees from its natural orientation.
-     */
-    static float getRotationDegrees(@NonNull final View view, final int deviceRotation) {
-        if (deviceRotation != ROTATION_AUTOMATIC) {
-            return SurfaceRotation.rotationDegreesFromSurfaceRotation(deviceRotation);
-        } else {
-            return getRotationDegrees(view);
-        }
-    }
-
-    /** Computes the rotation of a {@link View} in degrees from its natural orientation. */
-    static float getRotationDegrees(@NonNull final View view) {
-        final Display display = view.getDisplay();
-        if (display == null) {
-            return 0;
-        }
-        final int rotation = display.getRotation();
-        return SurfaceRotation.rotationDegreesFromSurfaceRotation(rotation);
-    }
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/ScaleTransform.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/ScaleTransform.java
deleted file mode 100644
index 2b2b6f4..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/ScaleTransform.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform;
-
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.camera.view.preview.transform.transformation.ScaleTransformation;
-
-/**
- * Computes the scale on both the x and y axes to uniformly scale up or down a view inside its
- * container, so that it entirely fills it, or is entirely container within it.
- */
-final class ScaleTransform {
-
-    private ScaleTransform() {
-    }
-
-    /**
-     * Computes the scale on both the x and y axes so that the view can uniformly fill its
-     * container.
-     */
-    static ScaleTransformation fill(@NonNull final View container, @NonNull final View view,
-            final int deviceRotation) {
-        return computeScale(container, view, Math::max, deviceRotation);
-    }
-
-    /**
-     * Computes the scale on both the x and y axes so that the view can uniformly fit inside its
-     * container.
-     */
-    static ScaleTransformation fit(@NonNull final View container, @NonNull final View view,
-            final int deviceRotation) {
-        return computeScale(container, view, Math::min, deviceRotation);
-    }
-
-    private static ScaleTransformation computeScale(@NonNull final View container,
-            @NonNull final View view, @NonNull final FloatBiFunction function,
-            final int deviceRotation) {
-        // Scaling only makes sense when none of the dimensions are equal to zero. In the
-        // opposite case, a default scale of 1 is returned,
-        if (container.getWidth() == 0 || container.getHeight() == 0 || view.getWidth() == 0
-                || view.getHeight() == 0) {
-            return new ScaleTransformation(1);
-        }
-
-        final int rotationDegrees = (int) RotationTransform.getRotationDegrees(view,
-                deviceRotation);
-        float bufferRotatedWidth;
-        float bufferRotatedHeight;
-        if (rotationDegrees == 0 || rotationDegrees == 180) {
-            bufferRotatedWidth = view.getWidth() * view.getScaleX();
-            bufferRotatedHeight = view.getHeight() * view.getScaleY();
-        } else {
-            bufferRotatedWidth = view.getHeight() * view.getScaleY();
-            bufferRotatedHeight = view.getWidth() * view.getScaleX();
-        }
-
-        final float scale = function.apply(container.getWidth() / bufferRotatedWidth,
-                container.getHeight() / bufferRotatedHeight);
-        return new ScaleTransformation(scale);
-    }
-
-    private interface FloatBiFunction {
-        float apply(float a, float b);
-    }
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/ScaleTypeTransform.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/ScaleTypeTransform.java
deleted file mode 100644
index 85bf472..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/ScaleTypeTransform.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform;
-
-import android.util.Pair;
-import android.view.Surface;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.camera.view.PreviewView;
-import androidx.camera.view.preview.transform.transformation.ScaleTransformation;
-import androidx.camera.view.preview.transform.transformation.Transformation;
-import androidx.camera.view.preview.transform.transformation.TranslationTransformation;
-
-final class ScaleTypeTransform {
-
-    private ScaleTypeTransform() {
-    }
-
-    /**
-     * Converts a {@link PreviewView.ScaleType} to a {@link Transformation}.
-     *
-     * @param container      Preview container
-     * @param view           Preview view (a {@link android.view.TextureView} or
-     *                       {@link android.view.SurfaceView})
-     * @param scaleType      The desired {@link PreviewView.ScaleType}.
-     * @param deviceRotation If the app is not running in remote display mode, set the parameter
-     *                       as {@link RotationTransform#ROTATION_AUTOMATIC}. Then, the rotation
-     *                       value queried from the preview will be used to do the transformation
-     *                       calculations. If the app is running in remote display mode, the
-     *                       device rotation value needs to be provided to make the result be
-     *                       rotated into correct orientation. The device rotation should be
-     *                       obtained from {@link android.view.OrientationEventListener} and
-     *                       needs to be converted into {@link Surface#ROTATION_0},
-     *                       {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180}, or
-     *                       {@link Surface#ROTATION_270}.
-     */
-    static Transformation getTransformation(@NonNull final View container, @NonNull final View view,
-            @NonNull final PreviewView.ScaleType scaleType, final int deviceRotation) {
-        final Transformation scale = getScale(container, view, scaleType, deviceRotation);
-
-        // Use the current preview scale AND the scale that's about to be applied to it to figure
-        // out how to position the preview in its container
-        final Pair<Float, Float> scaleXY = new Pair<>(view.getScaleX() * scale.getScaleX(),
-                view.getScaleY() * scale.getScaleY());
-        final Transformation translation = getScaledTranslation(container, view, scaleType,
-                scaleXY, deviceRotation);
-
-        return scale.add(translation);
-    }
-
-    private static ScaleTransformation getScale(@NonNull final View container,
-            @NonNull final View view, @NonNull final PreviewView.ScaleType scaleType,
-            final int deviceRotation) {
-        switch (scaleType) {
-            case FILL_START:
-            case FILL_CENTER:
-            case FILL_END:
-                return ScaleTransform.fill(container, view, deviceRotation);
-            case FIT_START:
-            case FIT_CENTER:
-            case FIT_END:
-                return ScaleTransform.fit(container, view, deviceRotation);
-            default:
-                throw new IllegalArgumentException("Unknown scale type " + scaleType);
-        }
-    }
-
-    private static TranslationTransformation getScaledTranslation(@NonNull final View container,
-            @NonNull final View view, @NonNull final PreviewView.ScaleType scaleType,
-            @NonNull final Pair<Float, Float> scaleXY, final int deviceRotation) {
-        switch (scaleType) {
-            case FILL_START:
-            case FIT_START:
-                return TranslationTransform.start(view, scaleXY, deviceRotation);
-            case FILL_CENTER:
-            case FIT_CENTER:
-                return TranslationTransform.center(container, view);
-            case FILL_END:
-            case FIT_END:
-                return TranslationTransform.end(container, view, scaleXY, deviceRotation);
-            default:
-                throw new IllegalArgumentException("Unknown scale type " + scaleType);
-        }
-    }
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/SurfaceRotation.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/SurfaceRotation.java
deleted file mode 100644
index 6428107..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/SurfaceRotation.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform;
-
-import android.view.Surface;
-
-final class SurfaceRotation {
-
-    private SurfaceRotation() {
-    }
-
-    /**
-     * Get the degrees of a rotation from the {@link Surface} constants.
-     *
-     * <p>
-     * Valid values for the relative rotation are {@link Surface#ROTATION_0}, {@link
-     * Surface#ROTATION_90}, {@link Surface#ROTATION_180},
-     * {@link Surface#ROTATION_270}.
-     * </p>
-     */
-    static int rotationDegreesFromSurfaceRotation(final int rotationConstant) {
-        switch (rotationConstant) {
-            case Surface.ROTATION_0:
-                return 0;
-            case Surface.ROTATION_90:
-                return 90;
-            case Surface.ROTATION_180:
-                return 180;
-            case Surface.ROTATION_270:
-                return 270;
-            default:
-                throw new UnsupportedOperationException(
-                        "Unsupported surface rotation constant: " + rotationConstant);
-        }
-    }
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/TranslationTransform.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/TranslationTransform.java
deleted file mode 100644
index 0be399d..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/TranslationTransform.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform;
-
-import android.util.Pair;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.camera.view.preview.transform.transformation.TranslationTransformation;
-
-/**
- * Computes the horizontal and vertical translations by which the preview needs to be translated
- * to position it at the start, center or end of its parent.
- * <p>
- * The start represents the top left corner in a left-to-right (LTR) layout, or the top right
- * corner in a right-to-left (RTL) layout.
- * <p>
- * The end represents the bottom right corner in a left-to-right (LTR) layout, or the bottom left
- * corner in a right-to-left (RTL) layout.
- */
-final class TranslationTransform {
-
-    private TranslationTransform() {
-    }
-
-    /**
-     * Computes the horizontal and vertical translations to set on {@code view} to align it to the
-     * start of its parent {@code container}.
-     * <p>
-     * The start represents the top left corner in a left-to-right (LTR) layout, or the top right
-     * corner in a right-to-left (RTL) layout.
-     */
-    static TranslationTransformation start(@NonNull final View view,
-            @NonNull final Pair<Float, Float> scaleXY, final int deviceRotation) {
-        if (view.getWidth() == 0 || view.getHeight() == 0) {
-            return new TranslationTransformation(0, 0);
-        }
-
-        // Scaled width and height of the view
-        final int scaledWidth = (int) (view.getWidth() * scaleXY.first);
-        final int scaledHeight = (int) (view.getHeight() * scaleXY.second);
-
-        final int viewRotationDegrees = (int) RotationTransform.getRotationDegrees(view,
-                deviceRotation);
-        final boolean isPortrait = viewRotationDegrees == 0 || viewRotationDegrees == 180;
-
-        // Coordinates of the view's center after the `start` translation
-        final int targetCenterX;
-        final int targetCenterY;
-        if (isPortrait) {
-            targetCenterX = scaledWidth / 2;
-            targetCenterY = scaledHeight / 2;
-        } else {
-            targetCenterX = scaledHeight / 2;
-            targetCenterY = scaledWidth / 2;
-        }
-
-        // Current coordinates of the view's center
-        final int currentCenterX = view.getWidth() / 2;
-        final int currentCenterY = view.getHeight() / 2;
-
-        final int transX = reverseIfRTLLayout(view, targetCenterX - currentCenterX);
-        final int transY = targetCenterY - currentCenterY;
-        return new TranslationTransformation(transX, transY);
-    }
-
-    /**
-     * Computes the horizontal and vertical translations to set on {@code view} to center it in its
-     * parent {@code container}.
-     */
-    static TranslationTransformation center(@NonNull final View container,
-            @NonNull final View view) {
-        if (view.getWidth() == 0 || view.getHeight() == 0) {
-            return new TranslationTransformation(0, 0);
-        }
-
-        // Coordinates of the view's center after the `center` translation
-        final int targetCenterX = container.getWidth() / 2;
-        final int targetCenterY = container.getHeight() / 2;
-
-        // Current coordinates of the view's center
-        final int currentCenterX = view.getWidth() / 2;
-        final int currentCenterY = view.getHeight() / 2;
-
-        final int transX = reverseIfRTLLayout(view, targetCenterX - currentCenterX);
-        final int transY = targetCenterY - currentCenterY;
-        return new TranslationTransformation(transX, transY);
-    }
-
-    /**
-     * Computes the horizontal and vertical translations to set on {@code view} to align it to the
-     * end of its parent {@code container}.
-     * <p>
-     * The end represents the bottom right corner in a left-to-right (LTR) layout, or the bottom
-     * left corner in a right-to-left (RTL) layout.
-     */
-    static TranslationTransformation end(@NonNull final View container, @NonNull final View view,
-            @NonNull final Pair<Float, Float> scaleXY, final int deviceRotation) {
-        if (view.getWidth() == 0 || view.getHeight() == 0) {
-            return new TranslationTransformation(0, 0);
-        }
-
-        // Coordinates of the bottom right corner of the container
-        final int endX = container.getWidth();
-        final int endY = container.getHeight();
-
-        // Scaled width and height of the view
-        final int scaledWidth = (int) (view.getWidth() * scaleXY.first);
-        final int scaledHeight = (int) (view.getHeight() * scaleXY.second);
-
-        final int viewRotationDegrees = (int) RotationTransform.getRotationDegrees(view,
-                deviceRotation);
-        final boolean isPortrait = viewRotationDegrees == 0 || viewRotationDegrees == 180;
-
-        // Coordinates of the view's center after the `end` translation
-        final int targetCenterX;
-        final int targetCenterY;
-        if (isPortrait) {
-            targetCenterX = endX - (scaledWidth / 2);
-            targetCenterY = endY - (scaledHeight / 2);
-        } else {
-            targetCenterX = endX - (scaledHeight / 2);
-            targetCenterY = endY - (scaledWidth / 2);
-        }
-
-        // Current coordinates of the view's center
-        final int currentCenterX = view.getWidth() / 2;
-        final int currentCenterY = view.getHeight() / 2;
-
-        final int transX = reverseIfRTLLayout(view, targetCenterX - currentCenterX);
-        final int transY = targetCenterY - currentCenterY;
-        return new TranslationTransformation(transX, transY);
-    }
-
-    /**
-     * Reverses a horizontal translation if the {@code view} is in a right-to-left (RTL) layout.
-     *
-     * @return The passed in horizontal translation if the layout is left-to-right (LTR), or its
-     * reverse if the layout is right-to-left (RTL).
-     */
-    private static int reverseIfRTLLayout(@NonNull final View view, int transX) {
-        final boolean isRTLDirection = view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        return isRTLDirection ? -transX : transX;
-    }
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/PreviewCorrectionTransformation.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/PreviewCorrectionTransformation.java
deleted file mode 100644
index 6ab11f7..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/PreviewCorrectionTransformation.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform.transformation;
-
-
-import androidx.annotation.RestrictTo;
-
-/**
- * A {@link Transformation} used to correct the camera preview.
- *
- * It only allows setting the scale on the x and y axes and the rotation, since these are the
- * operations involved when correcting the preview.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class PreviewCorrectionTransformation extends Transformation {
-
-    /**
-     * Creates a {@link Transformation} that corrects the preview. It doesn't translate the
-     * preview, keeping it at its initial position of (0, 0).
-     */
-    public PreviewCorrectionTransformation(float scaleX, float scaleY, float rotation) {
-        super(scaleX, scaleY, 0, 0, rotation);
-    }
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/ScaleTransformation.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/ScaleTransformation.java
deleted file mode 100644
index da113f0..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/ScaleTransformation.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform.transformation;
-
-import androidx.annotation.RestrictTo;
-
-/**
- * A {@link Transformation} used for scaling on the x and y axes.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public final class ScaleTransformation extends Transformation {
-
-    public ScaleTransformation(float scale) {
-        super(scale, scale, 0, 0, 0);
-    }
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/Transformation.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/Transformation.java
deleted file mode 100644
index d3bf25c..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/Transformation.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform.transformation;
-
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-/**
- * Contains the required information to transform a camera preview.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class Transformation {
-
-    private final float mScaleX;
-    private final float mScaleY;
-    private final float mTransX;
-    private final float mTransY;
-    private final float mRotation;
-
-    public Transformation() {
-        this(1, 1, 0, 0, 0);
-    }
-
-    public Transformation(final float scaleX, final float scaleY, final float transX,
-            final float transY, final float rotation) {
-        this.mScaleX = scaleX;
-        this.mScaleY = scaleY;
-        this.mTransX = transX;
-        this.mTransY = transY;
-        this.mRotation = rotation;
-    }
-
-    public float getScaleX() {
-        return mScaleX;
-    }
-
-    public float getScaleY() {
-        return mScaleY;
-    }
-
-    public float getTransX() {
-        return mTransX;
-    }
-
-    public float getTransY() {
-        return mTransY;
-    }
-
-    public float getRotation() {
-        return mRotation;
-    }
-
-    /** Performs the `sum` of two {@link Transformation} instances. */
-    @NonNull
-    public Transformation add(@NonNull final Transformation other) {
-        return new Transformation(mScaleX * other.mScaleX,
-                mScaleY * other.mScaleY,
-                mTransX + other.mTransX,
-                mTransY + other.mTransY,
-                mRotation + other.mRotation);
-    }
-
-    /** Performs the `subtraction` of two {@link Transformation} instances. */
-    @NonNull
-    public Transformation subtract(@NonNull final Transformation other) {
-        return new Transformation(mScaleX / other.mScaleX,
-                mScaleY / other.mScaleY,
-                mTransX - other.mTransX,
-                mTransY - other.mTransY,
-                mRotation - other.mRotation);
-    }
-
-    /** Returns a {@link Transformation} that represents the current state of the {@link View}. */
-    @NonNull
-    public static Transformation getTransformation(@NonNull final View view) {
-        return new Transformation(view.getScaleX(), view.getScaleY(), view.getTranslationX(),
-                view.getTranslationY(), view.getRotation());
-    }
-}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/TranslationTransformation.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/TranslationTransformation.java
deleted file mode 100644
index 4b214ab..0000000
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/transformation/TranslationTransformation.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform.transformation;
-
-
-import androidx.annotation.RestrictTo;
-
-/**
- * A {@link Transformation} used for positioning.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public final class TranslationTransformation extends Transformation {
-
-    public TranslationTransformation(float transX, float transY) {
-        super(1, 1, transX, transY, 0);
-    }
-}
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/PreviewViewTest.java b/camera/camera-view/src/test/java/androidx/camera/view/PreviewViewTest.java
deleted file mode 100644
index ee238c9..0000000
--- a/camera/camera-view/src/test/java/androidx/camera/view/PreviewViewTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view;
-
-import static android.os.Looper.getMainLooper;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assume.assumeTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
-
-import android.content.Context;
-import android.os.Build;
-import android.util.Size;
-import android.view.WindowManager;
-
-import androidx.camera.core.CameraInfo;
-import androidx.camera.core.Preview;
-import androidx.camera.core.SurfaceRequest;
-import androidx.camera.core.impl.CameraInfoInternal;
-import androidx.camera.testing.fakes.FakeCamera;
-import androidx.test.core.app.ApplicationProvider;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.internal.DoNotInstrument;
-import org.robolectric.shadows.ShadowDisplayManager;
-
-@RunWith(RobolectricTestRunner.class)
-@DoNotInstrument
-@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-public class PreviewViewTest {
-
-    private final Context mContext = ApplicationProvider.getApplicationContext();
-
-    private SurfaceRequest mSurfaceRequest;
-
-    private SurfaceRequest createSurfaceRequest(CameraInfo cameraInfo) {
-        FakeCamera fakeCamera = spy(new FakeCamera());
-        when(fakeCamera.getCameraInfo()).thenReturn(cameraInfo);
-        return new SurfaceRequest(new Size(640, 480), fakeCamera, false);
-    }
-
-    @After
-    public void tearDown() {
-        if (mSurfaceRequest != null) {
-            mSurfaceRequest.willNotProvideSurface();
-            // Ensure all successful requests have their returned future finish.
-            mSurfaceRequest.getDeferrableSurface().close();
-            mSurfaceRequest = null;
-        }
-    }
-
-    private CameraInfoInternal createCameraInfo(String implementationType) {
-        final CameraInfoInternal cameraInfo = mock(CameraInfoInternal.class);
-        when(cameraInfo.getImplementationType()).thenReturn(implementationType);
-        return cameraInfo;
-    }
-
-    @Test
-    public void forceUseTextureViewMode_whenNonLegacyDevice_andInRemoteDisplayMode() {
-        // The remote display simulation can only work from sdk 21 to 25. Limit to 24 because the
-        // test may be failed when running presubmit in API level 25.
-        assumeTrue(Build.VERSION.SDK_INT <= 24);
-        final PreviewView previewView = new PreviewView(mContext);
-
-        // Provides mock CameraInfo to make the device return non-legacy type.
-        final CameraInfo cameraInfo = createCameraInfo(CameraInfo.IMPLEMENTATION_TYPE_CAMERA2);
-
-        // Simulates the remote display mode by adding an additional display and returns the
-        // second display's id when PreviewView is querying the default display's id.
-        int secondDisplayId = addNewDisplay();
-        WindowManager windowManager =
-                (WindowManager) ApplicationProvider.getApplicationContext().getSystemService(
-                        Context.WINDOW_SERVICE);
-        shadowOf(windowManager.getDefaultDisplay()).setDisplayId(secondDisplayId);
-
-        previewView.setImplementationMode(PreviewView.ImplementationMode.PERFORMANCE);
-        Preview.SurfaceProvider surfaceProvider = previewView.getSurfaceProvider();
-        mSurfaceRequest = createSurfaceRequest(cameraInfo);
-        surfaceProvider.onSurfaceRequested(mSurfaceRequest);
-        shadowOf(getMainLooper()).idle();
-        assertThat(previewView.mImplementation).isInstanceOf(TextureViewImplementation.class);
-    }
-
-    @Test
-    public void forceUseTextureViewMode_whenLegacyDevice_andInRemoteDisplayMode() {
-        // The remote display simulation can only work from sdk 21 to 25. Limit to 24 because the
-        // test may be failed when running presubmit in API level 25.
-        assumeTrue(Build.VERSION.SDK_INT <= 24);
-        final PreviewView previewView = new PreviewView(mContext);
-
-        // Provides mock CameraInfo to make the device return legacy type.
-        final CameraInfo cameraInfo = createCameraInfo(
-                CameraInfo.IMPLEMENTATION_TYPE_CAMERA2_LEGACY);
-
-        // Simulates the remote display mode by adding an additional display and returns the
-        // second display's id when PreviewView is querying the default display's id.
-        int secondDisplayId = addNewDisplay();
-        WindowManager windowManager =
-                (WindowManager) ApplicationProvider.getApplicationContext().getSystemService(
-                        Context.WINDOW_SERVICE);
-        shadowOf(windowManager.getDefaultDisplay()).setDisplayId(secondDisplayId);
-
-        previewView.setImplementationMode(PreviewView.ImplementationMode.PERFORMANCE);
-        Preview.SurfaceProvider surfaceProvider = previewView.getSurfaceProvider();
-        mSurfaceRequest = createSurfaceRequest(cameraInfo);
-        surfaceProvider.onSurfaceRequested(mSurfaceRequest);
-        shadowOf(getMainLooper()).idle();
-        assertThat(previewView.mImplementation).isInstanceOf(TextureViewImplementation.class);
-    }
-
-    private int addNewDisplay() {
-        String displayQualifierString = String.format("w%ddp-h%ddp", 480, 640);
-        ShadowDisplayManager.addDisplay(displayQualifierString);
-        return ShadowDisplayManager.addDisplay(displayQualifierString);
-    }
-}
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/RotationTransformTest.java b/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/RotationTransformTest.java
deleted file mode 100644
index 6893175..0000000
--- a/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/RotationTransformTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.os.Build;
-import android.view.Display;
-import android.view.Surface;
-import android.view.View;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.internal.DoNotInstrument;
-
-@RunWith(RobolectricTestRunner.class)
-@DoNotInstrument
-@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-public class RotationTransformTest {
-    private final int[] mPossibleRotations = {Surface.ROTATION_0, Surface.ROTATION_90,
-            Surface.ROTATION_180, Surface.ROTATION_270};
-
-    @Test
-    public void rotation_0() {
-        final View view = setUpView(Surface.ROTATION_0);
-        assertThat(RotationTransform.getRotationDegrees(view)).isEqualTo(0);
-    }
-
-    @Test
-    public void rotation_90() {
-        final View view = setUpView(Surface.ROTATION_90);
-        assertThat(RotationTransform.getRotationDegrees(view)).isEqualTo(90);
-    }
-
-    @Test
-    public void rotation_180() {
-        final View view = setUpView(Surface.ROTATION_180);
-        assertThat(RotationTransform.getRotationDegrees(view)).isEqualTo(180);
-    }
-
-    @Test
-    public void rotation_270() {
-        final View view = setUpView(Surface.ROTATION_270);
-        assertThat(RotationTransform.getRotationDegrees(view)).isEqualTo(270);
-    }
-
-    @Test
-    public void viewAndDeviceRotationCombinations() {
-        for (int viewRotation : mPossibleRotations) {
-            final View view = setUpView(viewRotation);
-
-            // If the device rotation value is available, the return degrees will be calculated
-            // from the device rotation value.
-            for (int deviceRotation : mPossibleRotations) {
-                int deviceRotationDegrees =
-                        SurfaceRotation.rotationDegreesFromSurfaceRotation(deviceRotation);
-                assertThat(
-                        RotationTransform.getRotationDegrees(view, deviceRotation)).isEqualTo(
-                        deviceRotationDegrees);
-            }
-
-            // If the device rotation value is ROTATION_AUTOMATIC, the return degrees will be
-            // calculated from the view.
-            int viewRotationDegrees =
-                    SurfaceRotation.rotationDegreesFromSurfaceRotation(viewRotation);
-            assertThat(RotationTransform.getRotationDegrees(view,
-                    RotationTransform.ROTATION_AUTOMATIC)).isEqualTo(viewRotationDegrees);
-        }
-    }
-
-    @Test(expected = UnsupportedOperationException.class)
-    public void unknown_rotation() {
-        final View view = setUpView(-1);
-        RotationTransform.getRotationDegrees(view);
-    }
-
-    private View setUpView(int rotation) {
-        final View view = mock(View.class);
-        final Display display = mock(Display.class);
-        when(view.getDisplay()).thenReturn(display);
-        when(display.getRotation()).thenReturn(rotation);
-        return view;
-    }
-}
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/ScaleTransformTest.java b/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/ScaleTransformTest.java
deleted file mode 100644
index 5f416d6..0000000
--- a/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/ScaleTransformTest.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.os.Build;
-import android.view.Display;
-import android.view.Surface;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.camera.view.preview.transform.transformation.ScaleTransformation;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.internal.DoNotInstrument;
-
-@RunWith(RobolectricTestRunner.class)
-@DoNotInstrument
-@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-public class ScaleTransformTest {
-
-    @Test
-    public void fill_viewNotScaled_rotation0() {
-        final View container = setUpContainer(300, 400);
-        final View view = setUpView(200, 400, 1F, 1F, Surface.ROTATION_0);
-
-        final ScaleTransformation scale = ScaleTransform.fill(container, view,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtLeast(container.getWidth());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtLeast(container.getHeight());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getWidth()
-                || getScaledHeight(view) * scale.getScaleY() == container.getHeight()).isTrue();
-    }
-
-    @Test
-    public void fill_viewNotScaled_rotation90_deviceRotation0() {
-        final View container = setUpContainer(300, 400);
-        final View view = setUpView(200, 400, 1F, 1F, Surface.ROTATION_90);
-
-        final ScaleTransformation scale = ScaleTransform.fill(container, view, Surface.ROTATION_0);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtLeast(container.getWidth());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtLeast(container.getHeight());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getWidth()
-                || getScaledHeight(view) * scale.getScaleY() == container.getHeight()).isTrue();
-    }
-
-    @Test
-    public void fill_viewNotScaled_rotation90() {
-        final View container = setUpContainer(300, 400);
-        final View view = setUpView(200, 400, 1F, 1F, Surface.ROTATION_90);
-
-        final ScaleTransformation scale = ScaleTransform.fill(container, view,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtLeast(container.getHeight());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtLeast(container.getWidth());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getHeight()
-                || getScaledHeight(view) * scale.getScaleY() == container.getWidth()).isTrue();
-    }
-
-    @Test
-    public void fill_viewNotScaled_rotation0_deviceRotation90() {
-        final View container = setUpContainer(300, 400);
-        final View view = setUpView(200, 400, 1F, 1F, Surface.ROTATION_0);
-
-        final ScaleTransformation scale = ScaleTransform.fill(container, view,
-                Surface.ROTATION_90);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtLeast(container.getHeight());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtLeast(container.getWidth());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getHeight()
-                || getScaledHeight(view) * scale.getScaleY() == container.getWidth()).isTrue();
-    }
-
-    @Test
-    public void fill_viewScaled_rotation0() {
-        final View container = setUpContainer(300, 400);
-        final View view = setUpView(200, 400, 2F, 1.5F, Surface.ROTATION_0);
-
-        final ScaleTransformation scale = ScaleTransform.fill(container, view,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtLeast(container.getWidth());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtLeast(container.getHeight());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getWidth()
-                || getScaledHeight(view) * scale.getScaleY() == container.getHeight()).isTrue();
-    }
-
-    @Test
-    public void fill_viewScaled_rotation90_deviceRotation0() {
-        final View container = setUpContainer(300, 400);
-        final View view = setUpView(200, 400, 2F, 1.5F, Surface.ROTATION_90);
-
-        final ScaleTransformation scale = ScaleTransform.fill(container, view, Surface.ROTATION_0);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtLeast(container.getWidth());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtLeast(container.getHeight());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getWidth()
-                || getScaledHeight(view) * scale.getScaleY() == container.getHeight()).isTrue();
-    }
-
-    @Test
-    public void fill_viewScaled_rotation90() {
-        final View container = setUpContainer(300, 400);
-        final View view = setUpView(200, 400, 2F, 1.5F, Surface.ROTATION_90);
-
-        final ScaleTransformation scale = ScaleTransform.fill(container, view,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtLeast(container.getHeight());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtLeast(container.getWidth());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getHeight()
-                || getScaledHeight(view) * scale.getScaleY() == container.getWidth()).isTrue();
-    }
-
-    @Test
-    public void fill_viewScaled_rotation0_deviceRotation90() {
-        final View container = setUpContainer(300, 400);
-        final View view = setUpView(200, 400, 2F, 1.5F, Surface.ROTATION_0);
-
-        final ScaleTransformation scale = ScaleTransform.fill(container, view, Surface.ROTATION_90);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtLeast(container.getHeight());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtLeast(container.getWidth());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getHeight()
-                || getScaledHeight(view) * scale.getScaleY() == container.getWidth()).isTrue();
-    }
-
-    @Test
-    public void fit_viewNotScaled_rotation0() {
-        final View container = setUpContainer(200, 500);
-        final View view = setUpView(500, 1000, 1F, 1F, Surface.ROTATION_0);
-
-        final ScaleTransformation scale = ScaleTransform.fit(container, view,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtMost(container.getWidth());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtMost(container.getHeight());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getWidth()
-                || getScaledHeight(view) * scale.getScaleY() == container.getHeight()).isTrue();
-    }
-
-    @Test
-    public void fit_viewNotScaled_rotation90_deviceRotation0() {
-        final View container = setUpContainer(200, 500);
-        final View view = setUpView(500, 1000, 1F, 1F, Surface.ROTATION_90);
-
-        final ScaleTransformation scale = ScaleTransform.fit(container, view, Surface.ROTATION_0);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtMost(container.getWidth());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtMost(container.getHeight());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getWidth()
-                || getScaledHeight(view) * scale.getScaleY() == container.getHeight()).isTrue();
-    }
-
-    @Test
-    public void fit_viewNotScaled_rotation90() {
-        final View container = setUpContainer(200, 500);
-        final View view = setUpView(500, 1000, 1F, 1F, Surface.ROTATION_90);
-
-        final ScaleTransformation scale = ScaleTransform.fit(container, view,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtMost(container.getHeight());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtMost(container.getWidth());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getHeight()
-                || getScaledHeight(view) * scale.getScaleY() == container.getWidth()).isTrue();
-    }
-
-    @Test
-    public void fit_viewNotScaled_rotation0_deviceRotation90() {
-        final View container = setUpContainer(200, 500);
-        final View view = setUpView(500, 1000, 1F, 1F, Surface.ROTATION_0);
-
-        final ScaleTransformation scale = ScaleTransform.fit(container, view, Surface.ROTATION_90);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtMost(container.getHeight());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtMost(container.getWidth());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getHeight()
-                || getScaledHeight(view) * scale.getScaleY() == container.getWidth()).isTrue();
-    }
-
-    @Test
-    public void fit_viewScaled_rotation0() {
-        final View container = setUpContainer(200, 500);
-        final View view = setUpView(500, 1000, 0.5F, 2F, Surface.ROTATION_0);
-
-        final ScaleTransformation scale = ScaleTransform.fit(container, view,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtMost(container.getWidth());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtMost(container.getHeight());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getWidth()
-                || getScaledHeight(view) * scale.getScaleY() == container.getHeight()).isTrue();
-    }
-
-    @Test
-    public void fit_viewScaled_rotation90_deviceRotation0() {
-        final View container = setUpContainer(200, 500);
-        final View view = setUpView(500, 1000, 0.5F, 2F, Surface.ROTATION_90);
-
-        final ScaleTransformation scale = ScaleTransform.fit(container, view, Surface.ROTATION_0);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtMost(container.getWidth());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtMost(container.getHeight());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getWidth()
-                || getScaledHeight(view) * scale.getScaleY() == container.getHeight()).isTrue();
-    }
-
-    @Test
-    public void fit_viewScaled_rotation90() {
-        final View container = setUpContainer(200, 500);
-        final View view = setUpView(500, 1000, 0.5F, 2F, Surface.ROTATION_90);
-
-        final ScaleTransformation scale = ScaleTransform.fit(container, view,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtMost(container.getHeight());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtMost(container.getWidth());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getHeight()
-                || getScaledHeight(view) * scale.getScaleY() == container.getWidth()).isTrue();
-    }
-
-    @Test
-    public void fit_viewScaled_rotation0_deviceRotation90() {
-        final View container = setUpContainer(200, 500);
-        final View view = setUpView(500, 1000, 0.5F, 2F, Surface.ROTATION_0);
-
-        final ScaleTransformation scale = ScaleTransform.fit(container, view, Surface.ROTATION_90);
-
-        assertThat(scale.getScaleX()).isEqualTo(scale.getScaleY());
-        assertThat(getScaledWidth(view) * scale.getScaleX()).isAtMost(container.getHeight());
-        assertThat(getScaledHeight(view) * scale.getScaleY()).isAtMost(container.getWidth());
-        assertThat(getScaledWidth(view) * scale.getScaleX() == container.getHeight()
-                || getScaledHeight(view) * scale.getScaleY() == container.getWidth()).isTrue();
-    }
-
-    @NonNull
-    private View setUpContainer(int width, int height) {
-        final View container = mock(View.class);
-        when(container.getWidth()).thenReturn(width);
-        when(container.getHeight()).thenReturn(height);
-        return container;
-    }
-
-    @NonNull
-    private View setUpView(int width, int height, float scaleX, float scaleY, int rotation) {
-        final View view = mock(View.class);
-        when(view.getWidth()).thenReturn(width);
-        when(view.getHeight()).thenReturn(height);
-        when(view.getScaleX()).thenReturn(scaleX);
-        when(view.getScaleY()).thenReturn(scaleY);
-
-        final Display display = mock(Display.class);
-        when(view.getDisplay()).thenReturn(display);
-        when(display.getRotation()).thenReturn(rotation);
-
-        return view;
-    }
-
-    private float getScaledWidth(@NonNull final View view) {
-        return view.getWidth() * view.getScaleX();
-    }
-
-    private float getScaledHeight(@NonNull final View view) {
-        return view.getHeight() * view.getScaleY();
-    }
-}
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/TranslationTransformTest.java b/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/TranslationTransformTest.java
deleted file mode 100644
index f35cbbb..0000000
--- a/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/TranslationTransformTest.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform;
-
-import static android.view.View.LAYOUT_DIRECTION_LTR;
-import static android.view.View.LAYOUT_DIRECTION_RTL;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.os.Build;
-import android.util.Pair;
-import android.view.Display;
-import android.view.Surface;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.camera.view.preview.transform.transformation.TranslationTransformation;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.internal.DoNotInstrument;
-
-@RunWith(RobolectricTestRunner.class)
-@DoNotInstrument
-@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-public class TranslationTransformTest {
-
-    private static final int CONTAINER_WIDTH = 500;
-    private static final int CONTAINER_HEIGHT = 800;
-    private static final int VIEW_WIDTH = 200;
-    private static final int VIEW_HEIGHT = 300;
-
-    @Test
-    public void start_viewNotScaled_rotation0() {
-        final View view = setUpView(Surface.ROTATION_0);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(transformation.getTransX()).isEqualTo(0);
-        assertThat(transformation.getTransY()).isEqualTo(0);
-    }
-
-    @Test
-    public void start_viewNotScaled_rotation90_deviceRotation0() {
-        final View view = setUpView(Surface.ROTATION_90);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                Surface.ROTATION_0);
-
-        assertThat(transformation.getTransX()).isEqualTo(0);
-        assertThat(transformation.getTransY()).isEqualTo(0);
-    }
-
-    @Test
-    public void start_viewNotScaled_rotation90() {
-        final View view = setUpView(Surface.ROTATION_90);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(transformation.getTransX()).isEqualTo(50);
-        assertThat(transformation.getTransY()).isEqualTo(-50);
-    }
-
-    @Test
-    public void start_viewNotScaled_rotation0_deviceRotation90() {
-        final View view = setUpView(Surface.ROTATION_0);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                Surface.ROTATION_90);
-
-        assertThat(transformation.getTransX()).isEqualTo(50);
-        assertThat(transformation.getTransY()).isEqualTo(-50);
-    }
-
-    @Test
-    public void start_viewScaled_rotation0() {
-        final View view = setUpView(Surface.ROTATION_0);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(transformation.getTransX()).isEqualTo(100);
-        assertThat(transformation.getTransY()).isEqualTo(-75);
-    }
-
-    @Test
-    public void start_viewScaled_rotation90_deviceRotation0() {
-        final View view = setUpView(Surface.ROTATION_90);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                Surface.ROTATION_0);
-
-        assertThat(transformation.getTransX()).isEqualTo(100);
-        assertThat(transformation.getTransY()).isEqualTo(-75);
-    }
-
-    @Test
-    public void start_viewScaled_rotation90() {
-        final View view = setUpView(Surface.ROTATION_90);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(transformation.getTransX()).isEqualTo(-25);
-        assertThat(transformation.getTransY()).isEqualTo(50);
-    }
-
-    @Test
-    public void start_viewScaled_rotation0_deviceRotation90() {
-        final View view = setUpView(Surface.ROTATION_0);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                Surface.ROTATION_90);
-
-        assertThat(transformation.getTransX()).isEqualTo(-25);
-        assertThat(transformation.getTransY()).isEqualTo(50);
-    }
-
-    @Test
-    public void start_rtlLayout_rotation0() {
-        final View view = setUpView(Surface.ROTATION_0, LAYOUT_DIRECTION_RTL);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                Surface.ROTATION_0);
-
-        assertThat(transformation.getTransX()).isEqualTo(-100);
-        assertThat(transformation.getTransY()).isEqualTo(-75);
-    }
-
-    @Test
-    public void start_rtlLayout_rotation90_deviceRotation0() {
-        final View view = setUpView(Surface.ROTATION_90, LAYOUT_DIRECTION_RTL);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                Surface.ROTATION_0);
-
-        assertThat(transformation.getTransX()).isEqualTo(-100);
-        assertThat(transformation.getTransY()).isEqualTo(-75);
-    }
-
-    @Test
-    public void start_rtlLayout_rotation90() {
-        final View view = setUpView(Surface.ROTATION_90, LAYOUT_DIRECTION_RTL);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(transformation.getTransX()).isEqualTo(25);
-        assertThat(transformation.getTransY()).isEqualTo(50);
-    }
-
-    @Test
-    public void start_rtlLayout_rotation0_deviceRotation90() {
-        final View view = setUpView(Surface.ROTATION_0, LAYOUT_DIRECTION_RTL);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY,
-                Surface.ROTATION_90);
-
-        assertThat(transformation.getTransX()).isEqualTo(25);
-        assertThat(transformation.getTransY()).isEqualTo(50);
-    }
-
-    @Test
-    public void center_viewNotScaled_rotation0() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_0);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.center(container,
-                view);
-
-        assertThat(transformation.getTransX()).isEqualTo(150);
-        assertThat(transformation.getTransY()).isEqualTo(250);
-    }
-
-    @Test
-    public void center_viewNotScaled_rotation90() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_90);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.center(container,
-                view);
-
-        assertThat(transformation.getTransX()).isEqualTo(150);
-        assertThat(transformation.getTransY()).isEqualTo(250);
-    }
-
-    @Test
-    public void center_viewScaled_rotation0() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_0);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.center(container,
-                view);
-
-        assertThat(transformation.getTransX()).isEqualTo(150);
-        assertThat(transformation.getTransY()).isEqualTo(250);
-    }
-
-    @Test
-    public void center_viewScaled_rotation90() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_90);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.center(container,
-                view);
-
-        assertThat(transformation.getTransX()).isEqualTo(150);
-        assertThat(transformation.getTransY()).isEqualTo(250);
-    }
-
-    @Test
-    public void center_rtlLayout() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_0, LAYOUT_DIRECTION_RTL);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.center(container,
-                view);
-
-        assertThat(transformation.getTransX()).isEqualTo(-150);
-        assertThat(transformation.getTransY()).isEqualTo(250);
-    }
-
-    @Test
-    public void end_viewNotScaled_rotation0() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_0);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(transformation.getTransX()).isEqualTo(300);
-        assertThat(transformation.getTransY()).isEqualTo(500);
-    }
-
-    @Test
-    public void end_viewNotScaled_rotation90_deviceRotation0() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_90);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, Surface.ROTATION_0);
-
-        assertThat(transformation.getTransX()).isEqualTo(300);
-        assertThat(transformation.getTransY()).isEqualTo(500);
-    }
-
-    @Test
-    public void end_viewNotScaled_rotation90() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_90);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(transformation.getTransX()).isEqualTo(250);
-        assertThat(transformation.getTransY()).isEqualTo(550);
-    }
-
-    @Test
-    public void end_viewNotScaled_rotation0_deviceRotation90() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_0);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, Surface.ROTATION_90);
-
-        assertThat(transformation.getTransX()).isEqualTo(250);
-        assertThat(transformation.getTransY()).isEqualTo(550);
-    }
-
-    @Test
-    public void end_viewScaled_rotation0() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_0);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(transformation.getTransX()).isEqualTo(200);
-        assertThat(transformation.getTransY()).isEqualTo(575);
-    }
-
-    @Test
-    public void end_viewScaled_rotation90_deviceRotation0() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_90);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, Surface.ROTATION_0);
-
-        assertThat(transformation.getTransX()).isEqualTo(200);
-        assertThat(transformation.getTransY()).isEqualTo(575);
-    }
-
-    @Test
-    public void end_viewScaled_rotation90() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_90);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(transformation.getTransX()).isEqualTo(325);
-        assertThat(transformation.getTransY()).isEqualTo(450);
-    }
-
-    @Test
-    public void end_viewScaled_rotation0_deviceRotation90() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_0);
-        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, Surface.ROTATION_90);
-
-        assertThat(transformation.getTransX()).isEqualTo(325);
-        assertThat(transformation.getTransY()).isEqualTo(450);
-    }
-
-    @Test
-    public void end_rtlLayout_rotation0() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_0, LAYOUT_DIRECTION_RTL);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(transformation.getTransX()).isEqualTo(-300);
-        assertThat(transformation.getTransY()).isEqualTo(500);
-    }
-
-    @Test
-    public void end_rtlLayout_rotation90_deviceRotation0() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_90, LAYOUT_DIRECTION_RTL);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, Surface.ROTATION_0);
-
-        assertThat(transformation.getTransX()).isEqualTo(-300);
-        assertThat(transformation.getTransY()).isEqualTo(500);
-    }
-
-    @Test
-    public void end_rtlLayout_rotation90() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_90, LAYOUT_DIRECTION_RTL);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, RotationTransform.ROTATION_AUTOMATIC);
-
-        assertThat(transformation.getTransX()).isEqualTo(-250);
-        assertThat(transformation.getTransY()).isEqualTo(550);
-    }
-
-    @Test
-    public void end_rtlLayout_rotation0_deviceRotation90() {
-        final View container = setUpContainer();
-        final View view = setUpView(Surface.ROTATION_0, LAYOUT_DIRECTION_RTL);
-        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
-        final TranslationTransformation transformation = TranslationTransform.end(container, view,
-                scaleXY, Surface.ROTATION_90);
-
-        assertThat(transformation.getTransX()).isEqualTo(-250);
-        assertThat(transformation.getTransY()).isEqualTo(550);
-    }
-
-    @NonNull
-    private View setUpView(final int rotation) {
-        return setUpView(rotation, LAYOUT_DIRECTION_LTR);
-    }
-
-    @NonNull
-    private View setUpView(final int rotation, final int layoutDirection) {
-        final View view = mock(View.class);
-        when(view.getWidth()).thenReturn(VIEW_WIDTH);
-        when(view.getHeight()).thenReturn(VIEW_HEIGHT);
-        when(view.getLayoutDirection()).thenReturn(layoutDirection);
-
-        final Display display = mock(Display.class);
-        when(view.getDisplay()).thenReturn(display);
-        when(display.getRotation()).thenReturn(rotation);
-
-        return view;
-    }
-
-    @NonNull
-    private View setUpContainer() {
-        final View container = mock(View.class);
-        when(container.getWidth()).thenReturn(CONTAINER_WIDTH);
-        when(container.getHeight()).thenReturn(CONTAINER_HEIGHT);
-        return container;
-    }
-}
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/transformation/TransformationTest.java b/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/transformation/TransformationTest.java
deleted file mode 100644
index fa4d437..0000000
--- a/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/transformation/TransformationTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.view.preview.transform.transformation;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class TransformationTest {
-
-    @Test
-    public void addTransformations() {
-        final Transformation transformation = new Transformation(2.3F, 7.5F, 100F, 23.4F, 3.14F);
-        final Transformation other = new Transformation(12.3F, 0.5F, 0F, 0.24F, 90F);
-
-        final Transformation sum = transformation.add(other);
-
-        assertThat(sum.getScaleX()).isEqualTo(transformation.getScaleX() * other.getScaleX());
-        assertThat(sum.getScaleY()).isEqualTo(transformation.getScaleY() * other.getScaleY());
-        assertThat(sum.getTransX()).isEqualTo(transformation.getTransX() + other.getTransX());
-        assertThat(sum.getTransY()).isEqualTo(transformation.getTransY() + other.getTransY());
-        assertThat(sum.getRotation()).isEqualTo(transformation.getRotation() + other.getRotation());
-    }
-
-    @Test
-    public void subtractTransformations() {
-        final Transformation transformation = new Transformation(2.3F, 7.5F, 100F, 23.4F, 3.14F);
-        final Transformation other = new Transformation(12.3F, 0.5F, 0F, 0.24F, 90F);
-
-        final Transformation sum = transformation.subtract(other);
-
-        assertThat(sum.getScaleX()).isEqualTo(transformation.getScaleX() / other.getScaleX());
-        assertThat(sum.getScaleY()).isEqualTo(transformation.getScaleY() / other.getScaleY());
-        assertThat(sum.getTransX()).isEqualTo(transformation.getTransX() - other.getTransX());
-        assertThat(sum.getTransY()).isEqualTo(transformation.getTransY() - other.getTransY());
-        assertThat(sum.getRotation()).isEqualTo(transformation.getRotation() - other.getRotation());
-    }
-}
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
index 50db5e9..0b27c15 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
@@ -46,6 +46,7 @@
 import com.google.common.collect.ImmutableList
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import org.junit.After
 import org.junit.Assume
 import org.junit.Assume.assumeTrue
 import org.junit.Before
@@ -89,19 +90,30 @@
     )
 
     private val instrumentation = InstrumentationRegistry.getInstrumentation()
+    private lateinit var fragment: CameraControllerFragment
+    private lateinit var fragmentScenario: FragmentScenario<CameraControllerFragment>
 
     @Before
     fun setup() {
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
         // window before start the test.
         CoreAppTestUtil.prepareDeviceUI(instrumentation)
+        fragmentScenario = createFragmentScenario()
+        fragment = fragmentScenario.getFragment()
+    }
+
+    @After
+    fun tearDown() {
+        if (::fragmentScenario.isInitialized) {
+            fragmentScenario.moveToState(Lifecycle.State.DESTROYED)
+        }
     }
 
     @Test
     fun fragmentLaunch_cameraInitializationCompletes() {
         val semaphore = Semaphore(0)
         Futures.addCallback(
-            createFragmentScenario().getFragment().cameraController.initializationFuture,
+            fragment.cameraController.initializationFuture,
             object : FutureCallback<Void> {
                 override fun onSuccess(result: Void?) {
                     semaphore.release()
@@ -116,12 +128,11 @@
 
     @Test
     fun fragmentLaunch_receiveAnalysisFrames() {
-        createFragmentScenario().getFragment().assertAnalysisStreaming(true)
+        fragment.assertAnalysisStreaming(true)
     }
 
     @Test
     fun imageAnalysisDisabled_isNotStreaming() {
-        val fragment = createFragmentScenario().getFragment()
         fragment.assertAnalysisStreaming(true)
 
         onView(withId(R.id.analysis_enabled)).perform(click())
@@ -131,7 +142,6 @@
 
     @Test
     fun imageAnalysisDisabledAndEnabled_isStreaming() {
-        val fragment = createFragmentScenario().getFragment()
         fragment.assertAnalysisStreaming(true)
 
         onView(withId(R.id.analysis_enabled)).perform(click())
@@ -142,7 +152,6 @@
 
     @Test
     fun analyzerCleared_isNotStreaming() {
-        val fragment = createFragmentScenario().getFragment()
         fragment.assertAnalysisStreaming(true)
 
         instrumentation.runOnMainSync {
@@ -154,8 +163,6 @@
 
     @Test
     fun canSetAnalysisImageDepth() {
-        // Arrange.
-        val fragment = createFragmentScenario().getFragment()
         var currentDepth = 0
 
         // Act.
@@ -174,9 +181,6 @@
 
     @Test
     fun canSetAnalysisBackpressureStrategy() {
-        // Arrange.
-        val fragment = createFragmentScenario().getFragment()
-
         // Act.
         instrumentation.runOnMainSync {
             fragment.cameraController.imageAnalysisBackpressureStrategy =
@@ -200,7 +204,6 @@
         )
 
         // Arrange.
-        val fragment = createFragmentScenario().getFragment()
         fragment.assertPreviewIsStreaming()
         // Scaled down images to 10x10 bitmap to normalize and reduce computation.
         val width = 10
@@ -268,7 +271,6 @@
 
     @Test
     fun fragmentLaunched_canTakePicture() {
-        val fragment = createFragmentScenario().getFragment()
         fragment.assertPreviewIsStreaming()
         fragment.assertCanTakePicture()
     }
@@ -277,8 +279,6 @@
     fun captureDisabled_cannotTakePicture() {
         // Arrange.
         thrown.expectMessage("ImageCapture disabled")
-        val fragment = createFragmentScenario().getFragment()
-        fragment.assertPreviewIsStreaming()
 
         // Act.
         onView(withId(R.id.capture_enabled)).perform(click())
@@ -290,7 +290,6 @@
     @Test
     fun captureDisabledAndEnabled_canTakePicture() {
         // Arrange.
-        val fragment = createFragmentScenario().getFragment()
         fragment.assertPreviewIsStreaming()
 
         // Act.
@@ -304,14 +303,12 @@
 
     @Test
     fun previewViewRemoved_previewIsIdle() {
-        val fragment = createFragmentScenario().getFragment()
         onView(withId(R.id.remove_or_add)).perform(click())
         fragment.assertPreviewIsIdle()
     }
 
     @Test
     fun previewViewRemovedAndAdded_previewIsStreaming() {
-        val fragment = createFragmentScenario().getFragment()
         onView(withId(R.id.remove_or_add)).perform(click())
         onView(withId(R.id.remove_or_add)).perform(click())
         fragment.assertPreviewIsStreaming()
@@ -319,14 +316,12 @@
 
     @Test
     fun cameraToggled_previewIsStreaming() {
-        val fragment = createFragmentScenario().getFragment()
         onView(withId(R.id.camera_toggle)).perform(click())
         fragment.assertPreviewIsStreaming()
     }
 
     @Test
     fun cameraToggled_canTakePicture() {
-        val fragment = createFragmentScenario().getFragment()
         onView(withId(R.id.camera_toggle)).perform(click())
         fragment.assertPreviewIsStreaming()
         fragment.assertCanTakePicture()
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewFragmentTest.java b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewFragmentTest.java
index caae3ff..25d71b2 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewFragmentTest.java
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewFragmentTest.java
@@ -39,6 +39,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.GrantPermissionRule;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -67,6 +68,7 @@
             GrantPermissionRule.grant(android.Manifest.permission.RECORD_AUDIO);
 
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    private FragmentScenario<PreviewViewFragment> mScenario;
 
     @Before
     public void setup() throws CoreAppTestUtil.ForegroundOccupiedError {
@@ -75,40 +77,46 @@
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
         // window before start the test.
         CoreAppTestUtil.prepareDeviceUI(mInstrumentation);
+        mScenario = createScenario();
+    }
+
+    @After
+    public void tearDown() {
+        if (mScenario != null) {
+            mScenario.moveToState(Lifecycle.State.DESTROYED);
+        }
     }
 
     @Test
     public void checkPreviewUpdatedAfterDestroyRecreate() {
-        final FragmentScenario<PreviewViewFragment> scenario = createScenario();
-        assertPreviewUpdating(scenario);
+        assertPreviewUpdating(mScenario);
 
         // Recreate fragment
-        scenario.recreate();
-        assertPreviewUpdating(scenario);
+        mScenario.recreate();
+        assertPreviewUpdating(mScenario);
     }
 
     @Test
     public void checkPreviewUpdatedAfterStopResume_3Times() {
-        final FragmentScenario<PreviewViewFragment> scenario = createScenario();
-        assertPreviewUpdating(scenario);
+        assertPreviewUpdating(mScenario);
 
         // Stop the fragment
-        scenario.moveToState(Lifecycle.State.CREATED);
+        mScenario.moveToState(Lifecycle.State.CREATED);
         // Resume the fragment
-        scenario.moveToState(Lifecycle.State.RESUMED);
-        assertPreviewUpdating(scenario);
+        mScenario.moveToState(Lifecycle.State.RESUMED);
+        assertPreviewUpdating(mScenario);
 
         // Stop the fragment
-        scenario.moveToState(Lifecycle.State.CREATED);
+        mScenario.moveToState(Lifecycle.State.CREATED);
         // Resume the fragment
-        scenario.moveToState(Lifecycle.State.RESUMED);
-        assertPreviewUpdating(scenario);
+        mScenario.moveToState(Lifecycle.State.RESUMED);
+        assertPreviewUpdating(mScenario);
 
         // Stop the fragment
-        scenario.moveToState(Lifecycle.State.CREATED);
+        mScenario.moveToState(Lifecycle.State.CREATED);
         // Resume the fragment
-        scenario.moveToState(Lifecycle.State.RESUMED);
-        assertPreviewUpdating(scenario);
+        mScenario.moveToState(Lifecycle.State.RESUMED);
+        assertPreviewUpdating(mScenario);
     }
 
     @Test
@@ -116,17 +124,16 @@
         assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT));
         assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK));
 
-        final FragmentScenario<PreviewViewFragment> scenario = createScenario();
-        assertPreviewUpdating(scenario);
+        assertPreviewUpdating(mScenario);
 
         // Toggle camera
         onView(withId(R.id.toggle_camera)).perform(click());
 
         // Stop the fragment
-        scenario.moveToState(Lifecycle.State.CREATED);
+        mScenario.moveToState(Lifecycle.State.CREATED);
         // Resume the fragment
-        scenario.moveToState(Lifecycle.State.RESUMED);
-        assertPreviewUpdating(scenario);
+        mScenario.moveToState(Lifecycle.State.RESUMED);
+        assertPreviewUpdating(mScenario);
     }
 
     @Test
@@ -137,8 +144,6 @@
         assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT));
         assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK));
 
-        final FragmentScenario<PreviewViewFragment> scenario = createScenario();
-
         // Toggle camera. This is to toggle the camera from back to front.
         onView(withId(R.id.toggle_camera)).perform(click());
 
@@ -146,10 +151,10 @@
         onView(withId(R.id.toggle_camera)).perform(click());
 
         // Stop the fragment
-        scenario.moveToState(Lifecycle.State.CREATED);
+        mScenario.moveToState(Lifecycle.State.CREATED);
         // Resume the fragment
-        scenario.moveToState(Lifecycle.State.RESUMED);
-        assertPreviewUpdating(scenario);
+        mScenario.moveToState(Lifecycle.State.RESUMED);
+        assertPreviewUpdating(mScenario);
     }
 
     @Test
@@ -157,70 +162,65 @@
         assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT));
         assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK));
 
-        final FragmentScenario<PreviewViewFragment> scenario = createScenario();
-        assertPreviewUpdating(scenario);
+        assertPreviewUpdating(mScenario);
 
         for (int i = 0; i <= 10; i++) {
             onView(withId(R.id.toggle_camera)).perform(click());
         }
 
-        assertPreviewUpdating(scenario);
+        assertPreviewUpdating(mScenario);
     }
 
     @Test
     public void checkPreviewNotUpdatedAfterPreviewUnbound() {
-        final FragmentScenario<PreviewViewFragment> scenario = createScenario();
-        assertPreviewUpdating(scenario);
+        assertPreviewUpdating(mScenario);
 
         // Toggle visibility to unbind preview
         onView(withId(R.id.toggle_visibility)).perform(click());
-        assertPreviewNotUpdating(scenario);
+        assertPreviewNotUpdating(mScenario);
     }
 
     @Test
     public void checkPreviewUpdatedWhenSamePreviewViewAttachedToMultiplePreviewUseCases() {
-        final FragmentScenario<PreviewViewFragment> scenario = createScenario();
-        assertPreviewUpdating(scenario);
+        assertPreviewUpdating(mScenario);
 
         // Toggle visibility to unbind preview
         onView(withId(R.id.toggle_visibility)).perform(click());
         // Toggle visibility to bind new preview
         onView(withId(R.id.toggle_visibility)).perform(click());
-        assertPreviewUpdating(scenario);
+        assertPreviewUpdating(mScenario);
     }
 
     @Test
     public void checkSameScaleTypeAfterStopResume() {
-        final FragmentScenario<PreviewViewFragment> scenario = createScenario();
-        assertPreviewUpdating(scenario);
+        assertPreviewUpdating(mScenario);
 
         mInstrumentation.runOnMainSync(
-                () -> getPreviewView(scenario).setScaleType(PreviewView.ScaleType.FIT_END));
+                () -> getPreviewView(mScenario).setScaleType(PreviewView.ScaleType.FIT_END));
 
         // Stop the fragment
-        scenario.moveToState(Lifecycle.State.CREATED);
+        mScenario.moveToState(Lifecycle.State.CREATED);
         // Resume the fragment
-        scenario.moveToState(Lifecycle.State.RESUMED);
+        mScenario.moveToState(Lifecycle.State.RESUMED);
         mInstrumentation.runOnMainSync(
-                () -> assertThat(getPreviewView(scenario).getScaleType()).isEqualTo(
+                () -> assertThat(getPreviewView(mScenario).getScaleType()).isEqualTo(
                         PreviewView.ScaleType.FIT_END));
     }
 
     @Test
     public void checkSameImplementationModeAfterStopResume() {
-        final FragmentScenario<PreviewViewFragment> scenario = createScenario();
-        assertPreviewUpdating(scenario);
+        assertPreviewUpdating(mScenario);
 
         mInstrumentation.runOnMainSync(
-                () -> getPreviewView(scenario).setImplementationMode(
+                () -> getPreviewView(mScenario).setImplementationMode(
                         PreviewView.ImplementationMode.COMPATIBLE));
 
         // Stop the fragment
-        scenario.moveToState(Lifecycle.State.CREATED);
+        mScenario.moveToState(Lifecycle.State.CREATED);
         // Resume the fragment
-        scenario.moveToState(Lifecycle.State.RESUMED);
+        mScenario.moveToState(Lifecycle.State.RESUMED);
         mInstrumentation.runOnMainSync(
-                () -> assertThat(getPreviewView(scenario).getImplementationMode()).isEqualTo(
+                () -> assertThat(getPreviewView(mScenario).getImplementationMode()).isEqualTo(
                         PreviewView.ImplementationMode.COMPATIBLE));
     }
 
diff --git a/collection/OWNERS b/collection/OWNERS
index a02ebce..0e9710d 100644
--- a/collection/OWNERS
+++ b/collection/OWNERS
@@ -1,2 +1,3 @@
-jakew@google.com
-android@jakewharton.com
+lukhnos@google.com
+dustinlam@google.com
+yboyar@google.com
diff --git a/collection/collection/src/test/java/androidx/collection/LruCacheTest.java b/collection/collection/src/test/java/androidx/collection/LruCacheTest.java
new file mode 100644
index 0000000..ab173be
--- /dev/null
+++ b/collection/collection/src/test/java/androidx/collection/LruCacheTest.java
@@ -0,0 +1,512 @@
+/*
+ * 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.collection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(JUnit4.class)
+public class LruCacheTest {
+
+    private int mExpectedCreateCount;
+    private int mExpectedPutCount;
+    private int mExpectedHitCount;
+    private int mExpectedMissCount;
+    private int mExpectedEvictionCount;
+
+    @Test
+    public void testStatistics() {
+        LruCache<String, String> cache = new LruCache<String, String>(3);
+        assertStatistics(cache);
+        assertEquals(null, cache.put("a", "A"));
+        mExpectedPutCount++;
+        assertStatistics(cache);
+        assertHit(cache, "a", "A");
+        assertSnapshot(cache, "a", "A");
+        assertEquals(null, cache.put("b", "B"));
+        mExpectedPutCount++;
+        assertStatistics(cache);
+        assertHit(cache, "a", "A");
+        assertHit(cache, "b", "B");
+        assertSnapshot(cache, "a", "A", "b", "B");
+        assertEquals(null, cache.put("c", "C"));
+        mExpectedPutCount++;
+        assertStatistics(cache);
+        assertHit(cache, "a", "A");
+        assertHit(cache, "b", "B");
+        assertHit(cache, "c", "C");
+        assertSnapshot(cache, "a", "A", "b", "B", "c", "C");
+        assertEquals(null, cache.put("d", "D"));
+        mExpectedPutCount++;
+        mExpectedEvictionCount++; // a should have been evicted
+        assertStatistics(cache);
+        assertMiss(cache, "a");
+        assertHit(cache, "b", "B");
+        assertHit(cache, "c", "C");
+        assertHit(cache, "d", "D");
+        assertHit(cache, "b", "B");
+        assertHit(cache, "c", "C");
+        assertSnapshot(cache, "d", "D", "b", "B", "c", "C");
+        assertEquals(null, cache.put("e", "E"));
+        mExpectedPutCount++;
+        mExpectedEvictionCount++; // d should have been evicted
+        assertStatistics(cache);
+        assertMiss(cache, "d");
+        assertMiss(cache, "a");
+        assertHit(cache, "e", "E");
+        assertHit(cache, "b", "B");
+        assertHit(cache, "c", "C");
+        assertSnapshot(cache, "e", "E", "b", "B", "c", "C");
+    }
+
+    @Test
+    public void testStatisticsWithCreate() {
+        LruCache<String, String> cache = newCreatingCache();
+        assertStatistics(cache);
+        assertCreated(cache, "aa", "created-aa");
+        assertHit(cache, "aa", "created-aa");
+        assertSnapshot(cache, "aa", "created-aa");
+        assertCreated(cache, "bb", "created-bb");
+        assertMiss(cache, "c");
+        assertSnapshot(cache, "aa", "created-aa", "bb", "created-bb");
+        assertCreated(cache, "cc", "created-cc");
+        assertSnapshot(cache, "aa", "created-aa", "bb", "created-bb", "cc", "created-cc");
+        mExpectedEvictionCount++; // aa will be evicted
+        assertCreated(cache, "dd", "created-dd");
+        assertSnapshot(cache, "bb", "created-bb",  "cc", "created-cc", "dd", "created-dd");
+        mExpectedEvictionCount++; // bb will be evicted
+        assertCreated(cache, "aa", "created-aa");
+        assertSnapshot(cache, "cc", "created-cc", "dd", "created-dd", "aa", "created-aa");
+    }
+
+    @Test
+    public void testCreateOnCacheMiss() {
+        LruCache<String, String> cache = newCreatingCache();
+        String created = cache.get("aa");
+        assertEquals("created-aa", created);
+    }
+
+    @Test
+    public void testNoCreateOnCacheHit() {
+        LruCache<String, String> cache = newCreatingCache();
+        cache.put("aa", "put-aa");
+        assertEquals("put-aa", cache.get("aa"));
+    }
+
+    @Test
+    public void testConstructorDoesNotAllowZeroCacheSize() {
+        try {
+            new LruCache<String, String>(0);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testCannotPutNullKey() {
+        LruCache<String, String> cache = new LruCache<String, String>(3);
+        try {
+            cache.put(null, "A");
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    @Test
+    public void testCannotPutNullValue() {
+        LruCache<String, String> cache = new LruCache<String, String>(3);
+        try {
+            cache.put("a", null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    @Test
+    public void testToString() {
+        LruCache<String, String> cache = new LruCache<String, String>(3);
+        assertEquals("LruCache[maxSize=3,hits=0,misses=0,hitRate=0%]", cache.toString());
+        cache.put("a", "A");
+        cache.put("b", "B");
+        cache.put("c", "C");
+        cache.put("d", "D");
+        cache.get("a"); // miss
+        cache.get("b"); // hit
+        cache.get("c"); // hit
+        cache.get("d"); // hit
+        cache.get("e"); // miss
+        assertEquals("LruCache[maxSize=3,hits=3,misses=2,hitRate=60%]", cache.toString());
+    }
+
+    @Test
+    public void testEvictionWithSingletonCache() {
+        LruCache<String, String> cache = new LruCache<String, String>(1);
+        cache.put("a", "A");
+        cache.put("b", "B");
+        assertSnapshot(cache, "b", "B");
+    }
+
+    @Test
+    public void testEntryEvictedWhenFull() {
+        List<String> log = new ArrayList<String>();
+        LruCache<String, String> cache = newRemovalLogCache(log);
+        cache.put("a", "A");
+        cache.put("b", "B");
+        cache.put("c", "C");
+        assertEquals(Collections.<String>emptyList(), log);
+        cache.put("d", "D");
+        assertEquals(Arrays.asList("a=A"), log);
+    }
+
+    /**
+     * Replacing the value for a key doesn't cause an eviction but it does bring
+     * the replaced entry to the front of the queue.
+     */
+    @Test
+    public void testPutCauseEviction() {
+        List<String> log = new ArrayList<String>();
+        LruCache<String, String> cache = newRemovalLogCache(log);
+        cache.put("a", "A");
+        cache.put("b", "B");
+        cache.put("c", "C");
+        cache.put("b", "B2");
+        assertEquals(Arrays.asList("b=B>B2"), log);
+        assertSnapshot(cache, "a", "A", "c", "C", "b", "B2");
+    }
+
+    @Test
+    public void testCustomSizesImpactsSize() {
+        LruCache<String, String> cache = new LruCache<String, String>(10) {
+            @Override protected int sizeOf(String key, String value) {
+                return key.length() + value.length();
+            }
+        };
+        assertEquals(0, cache.size());
+        cache.put("a", "AA");
+        assertEquals(3, cache.size());
+        cache.put("b", "BBBB");
+        assertEquals(8, cache.size());
+        cache.put("a", "");
+        assertEquals(6, cache.size());
+    }
+
+    @Test
+    public void testEvictionWithCustomSizes() {
+        LruCache<String, String> cache = new LruCache<String, String>(4) {
+            @Override protected int sizeOf(String key, String value) {
+                return value.length();
+            }
+        };
+        cache.put("a", "AAAA");
+        assertSnapshot(cache, "a", "AAAA");
+        cache.put("b", "BBBB"); // should evict a
+        assertSnapshot(cache, "b", "BBBB");
+        cache.put("c", "CC"); // should evict b
+        assertSnapshot(cache, "c", "CC");
+        cache.put("d", "DD");
+        assertSnapshot(cache, "c", "CC", "d", "DD");
+        cache.put("e", "E"); // should evict c
+        assertSnapshot(cache, "d", "DD", "e", "E");
+        cache.put("f", "F");
+        assertSnapshot(cache, "d", "DD", "e", "E", "f", "F");
+        cache.put("g", "G"); // should evict d
+        assertSnapshot(cache, "e", "E", "f", "F", "g", "G");
+        cache.put("h", "H");
+        assertSnapshot(cache, "e", "E", "f", "F", "g", "G", "h", "H");
+        cache.put("i", "III"); // should evict e, f, and g
+        assertSnapshot(cache, "h", "H", "i", "III");
+        cache.put("j", "JJJ"); // should evict h and i
+        assertSnapshot(cache, "j", "JJJ");
+    }
+
+    @Test
+    public void testEvictionThrowsWhenSizesAreInconsistent() {
+        LruCache<String, int[]> cache = new LruCache<String, int[]>(4) {
+            @Override protected int sizeOf(String key, int[] value) {
+                return value[0];
+            }
+        };
+        int[] a = { 4 };
+        cache.put("a", a);
+        // get the cache size out of sync
+        a[0] = 1;
+        assertEquals(4, cache.size());
+        // evict something
+        try {
+            cache.put("b", new int[] { 2 });
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    @Test
+    public void testEvictionThrowsWhenSizesAreNegative() {
+        LruCache<String, String> cache = new LruCache<String, String>(4) {
+            @Override protected int sizeOf(String key, String value) {
+                return -1;
+            }
+        };
+        try {
+            cache.put("a", "A");
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    /**
+     * Naive caches evict at most one element at a time. This is problematic
+     * because evicting a small element may be insufficient to make room for a
+     * large element.
+     */
+    @Test
+    public void testDifferentElementSizes() {
+        LruCache<String, String> cache = new LruCache<String, String>(10) {
+            @Override protected int sizeOf(String key, String value) {
+                return value.length();
+            }
+        };
+        cache.put("a", "1");
+        cache.put("b", "12345678");
+        cache.put("c", "1");
+        assertSnapshot(cache, "a", "1", "b", "12345678", "c", "1");
+        cache.put("d", "12345678"); // should evict a and b
+        assertSnapshot(cache, "c", "1", "d", "12345678");
+        cache.put("e", "12345678"); // should evict c and d
+        assertSnapshot(cache, "e", "12345678");
+    }
+
+    @Test
+    public void testEvictAll() {
+        List<String> log = new ArrayList<String>();
+        LruCache<String, String> cache = newRemovalLogCache(log);
+        cache.put("a", "A");
+        cache.put("b", "B");
+        cache.put("c", "C");
+        cache.evictAll();
+        assertEquals(0, cache.size());
+        assertEquals(Arrays.asList("a=A", "b=B", "c=C"), log);
+    }
+
+    @Test
+    public void testEvictAllEvictsSizeZeroElements() {
+        LruCache<String, String> cache = new LruCache<String, String>(10) {
+            @Override protected int sizeOf(String key, String value) {
+                return 0;
+            }
+        };
+        cache.put("a", "A");
+        cache.put("b", "B");
+        cache.evictAll();
+        assertSnapshot(cache);
+    }
+
+    @Test
+    public void testRemoveWithCustomSizes() {
+        LruCache<String, String> cache = new LruCache<String, String>(10) {
+            @Override protected int sizeOf(String key, String value) {
+                return value.length();
+            }
+        };
+        cache.put("a", "123456");
+        cache.put("b", "1234");
+        cache.remove("a");
+        assertEquals(4, cache.size());
+    }
+
+    @Test
+    public void testRemoveAbsentElement() {
+        LruCache<String, String> cache = new LruCache<String, String>(10);
+        cache.put("a", "A");
+        cache.put("b", "B");
+        assertEquals(null, cache.remove("c"));
+        assertEquals(2, cache.size());
+    }
+
+    @Test
+    public void testRemoveNullThrows() {
+        LruCache<String, String> cache = new LruCache<String, String>(10);
+        try {
+            cache.remove(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    @Test
+    public void testRemoveCallsEntryRemoved() {
+        List<String> log = new ArrayList<String>();
+        LruCache<String, String> cache = newRemovalLogCache(log);
+        cache.put("a", "A");
+        cache.remove("a");
+        assertEquals(Arrays.asList("a=A>null"), log);
+    }
+
+    @Test
+    public void testPutCallsEntryRemoved() {
+        List<String> log = new ArrayList<String>();
+        LruCache<String, String> cache = newRemovalLogCache(log);
+        cache.put("a", "A");
+        cache.put("a", "A2");
+        assertEquals(Arrays.asList("a=A>A2"), log);
+    }
+
+    @Test
+    public void testEntryRemovedIsCalledWithoutSynchronization() {
+        LruCache<String, String> cache = new LruCache<String, String>(3) {
+            @Override protected void entryRemoved(
+                    boolean evicted, String key, String oldValue, String newValue) {
+                assertFalse(Thread.holdsLock(this));
+            }
+        };
+        cache.put("a", "A");
+        cache.put("a", "A2"); // replaced
+        cache.put("b", "B");
+        cache.put("c", "C");
+        cache.put("d", "D");  // single eviction
+        cache.remove("a");    // removed
+        cache.evictAll();     // multiple eviction
+    }
+
+    @Test
+    public void testCreateIsCalledWithoutSynchronization() {
+        LruCache<String, String> cache = new LruCache<String, String>(3) {
+            @Override protected String create(String key) {
+                assertFalse(Thread.holdsLock(this));
+                return null;
+            }
+        };
+        cache.get("a");
+    }
+
+    /**
+     * Test what happens when a value is added to the map while create is
+     * working. The map value should be returned by get(), and the created value
+     * should be released with entryRemoved().
+     */
+    @Test
+    public void testCreateWithConcurrentPut() {
+        final List<String> log = new ArrayList<String>();
+        LruCache<String, String> cache = new LruCache<String, String>(3) {
+            @Override protected String create(String key) {
+                put(key, "B");
+                return "A";
+            }
+            @Override protected void entryRemoved(
+                    boolean evicted, String key, String oldValue, String newValue) {
+                log.add(key + "=" + oldValue + ">" + newValue);
+            }
+        };
+        assertEquals("B", cache.get("a"));
+        assertEquals(Arrays.asList("a=A>B"), log);
+    }
+
+    /**
+     * Test what happens when two creates happen concurrently. The result from
+     * the first create to return is returned by both gets. The other created
+     * values should be released with entryRemove().
+     */
+    @Test
+    public void testCreateWithConcurrentCreate() {
+        final List<String> log = new ArrayList<String>();
+        LruCache<String, Integer> cache = new LruCache<String, Integer>(3) {
+            int mCallCount = 0;
+            @Override protected Integer create(String key) {
+                if (mCallCount++ == 0) {
+                    assertEquals(2, get(key).intValue());
+                    return 1;
+                } else {
+                    return 2;
+                }
+            }
+            @Override protected void entryRemoved(
+                    boolean evicted, String key, Integer oldValue, Integer newValue) {
+                log.add(key + "=" + oldValue + ">" + newValue);
+            }
+        };
+        assertEquals(2, cache.get("a").intValue());
+        assertEquals(Arrays.asList("a=1>2"), log);
+    }
+
+    private LruCache<String, String> newCreatingCache() {
+        return new LruCache<String, String>(3) {
+            @Override protected String create(String key) {
+                return (key.length() > 1) ? ("created-" + key) : null;
+            }
+        };
+    }
+
+    private LruCache<String, String> newRemovalLogCache(final List<String> log) {
+        return new LruCache<String, String>(3) {
+            @Override protected void entryRemoved(
+                    boolean evicted, String key, String oldValue, String newValue) {
+                String message = evicted
+                        ? (key + "=" + oldValue)
+                        : (key + "=" + oldValue + ">" + newValue);
+                log.add(message);
+            }
+        };
+    }
+
+    private void assertHit(LruCache<String, String> cache, String key, String value) {
+        assertEquals(value, cache.get(key));
+        mExpectedHitCount++;
+        assertStatistics(cache);
+    }
+
+    private void assertMiss(LruCache<String, String> cache, String key) {
+        assertEquals(null, cache.get(key));
+        mExpectedMissCount++;
+        assertStatistics(cache);
+    }
+
+    private void assertCreated(LruCache<String, String> cache, String key, String value) {
+        assertEquals(value, cache.get(key));
+        mExpectedMissCount++;
+        mExpectedCreateCount++;
+        assertStatistics(cache);
+    }
+
+    private void assertStatistics(LruCache<?, ?> cache) {
+        assertEquals("create count", mExpectedCreateCount, cache.createCount());
+        assertEquals("put count", mExpectedPutCount, cache.putCount());
+        assertEquals("hit count", mExpectedHitCount, cache.hitCount());
+        assertEquals("miss count", mExpectedMissCount, cache.missCount());
+        assertEquals("eviction count", mExpectedEvictionCount, cache.evictionCount());
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> void assertSnapshot(LruCache<T, T> cache, T... keysAndValues) {
+        List<T> actualKeysAndValues = new ArrayList<T>();
+        for (Map.Entry<T, T> entry : cache.snapshot().entrySet()) {
+            actualKeysAndValues.add(entry.getKey());
+            actualKeysAndValues.add(entry.getValue());
+        }
+        // assert using lists because order is important for LRUs
+        assertEquals(Arrays.asList(keysAndValues), actualKeysAndValues);
+    }
+}
diff --git a/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/AndroidViewDemos.kt b/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/AndroidViewDemos.kt
index 26b6d6a..26e38e4 100644
--- a/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/AndroidViewDemos.kt
+++ b/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/AndroidViewDemos.kt
@@ -17,6 +17,7 @@
 package androidx.compose.androidview.demos
 
 import androidx.compose.integration.demos.common.ActivityDemo
+import androidx.compose.integration.demos.common.ComposableDemo
 import androidx.compose.integration.demos.common.DemoCategory
 
 val AndroidViewDemos = DemoCategory(
@@ -25,6 +26,7 @@
         ComposeInAndroidDemos,
         AndroidInComposeDemos,
         ComplexTouchInterop,
-        ActivityDemo("WebComponent", WebComponentActivity::class)
+        ActivityDemo("WebComponent", WebComponentActivity::class),
+        ComposableDemo("TextField Interop") { EditTextInteropDemo() },
     )
 )
diff --git a/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/FocusInteropAndroidInCompose.kt b/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/FocusInteropAndroidInCompose.kt
new file mode 100644
index 0000000..be35f52
--- /dev/null
+++ b/compose/androidview/androidview/integration-tests/androidview-demos/src/main/java/androidx/compose/androidview/demos/FocusInteropAndroidInCompose.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.androidview.demos
+
+import android.annotation.SuppressLint
+import android.widget.EditText
+import android.widget.LinearLayout
+import android.widget.RelativeLayout
+import android.widget.TextView
+import androidx.compose.foundation.layout.Arrangement.SpaceEvenly
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.material.Text
+import androidx.compose.material.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Recomposer
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment.Companion.CenterVertically
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.ExperimentalFocus
+import androidx.compose.ui.platform.setContent
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+
+@OptIn(ExperimentalFocus::class)
+@Composable
+fun EditTextInteropDemo() {
+    Column {
+        Row(horizontalArrangement = SpaceEvenly, verticalAlignment = CenterVertically) {
+            Text("TextField in Compose:")
+            val text = remember { mutableStateOf(TextFieldValue()) }
+            TextField(text.value, onValueChange = { text.value = it })
+        }
+        Spacer(Modifier.height(20.dp))
+        Row(horizontalArrangement = SpaceEvenly, verticalAlignment = CenterVertically) {
+            AndroidView({
+                LinearLayout(it).apply {
+                    this.orientation = LinearLayout.VERTICAL
+                    addView(
+                        LinearLayout(it).apply {
+                            addView(
+                                TextView(it).apply {
+                                    @SuppressLint("SetTextI18n")
+                                    text = "EditText within AndroidView:"
+                                }
+                            )
+                            addView(EditText(it).apply { width = 500 })
+                        }
+                    )
+                    addView(
+                        LinearLayout(it).apply {
+                            addView(
+                                TextView(it).apply {
+                                    @SuppressLint("SetTextI18n")
+                                    text = "TextField within AndroidView:"
+                                }
+                            )
+                            addView(
+                                RelativeLayout(it).apply {
+                                    setContent(Recomposer.current()) {
+                                        val text = remember { mutableStateOf(TextFieldValue()) }
+                                        TextField(text.value, onValueChange = { text.value = it })
+                                    }
+                                }
+                            )
+                        }
+                    )
+                }
+            })
+        }
+    }
+}
diff --git a/compose/foundation/foundation-layout/lint-baseline.xml b/compose/foundation/foundation-layout/lint-baseline.xml
index 7ee29fa..93c34dac 100644
--- a/compose/foundation/foundation-layout/lint-baseline.xml
+++ b/compose/foundation/foundation-layout/lint-baseline.xml
@@ -8,30 +8,8 @@
         errorLine2="                         ^">
         <location
             file="src/androidMain/kotlin/androidx/compose/foundation/layout/ConstraintLayout.kt"
-            line="533"
+            line="534"
             column="26"/>
     </issue>
 
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="fun Modifier.preferredWidth(intrinsicSize: IntrinsicSize) = when (intrinsicSize) {"
-        errorLine2="                                                            ~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/foundation/layout/Intrinsic.kt"
-            line="47"
-            column="61"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="fun Modifier.preferredHeight(intrinsicSize: IntrinsicSize) = when (intrinsicSize) {"
-        errorLine2="                                                             ~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/foundation/layout/Intrinsic.kt"
-            line="68"
-            column="62"/>
-    </issue>
-
 </issues>
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutAlignTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutAlignTest.kt
index 568d9d4..5e1e41f 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutAlignTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/LayoutAlignTest.kt
@@ -274,7 +274,7 @@
                         ).enforce(incomingConstraints)
                         val placeable = measurable.measure(constraints)
                         layout(placeable.width, placeable.height) {
-                            placeable.placeRelative(Offset.Zero)
+                            placeable.placeRelative(IntOffset.Zero)
                         }
                     }
                 )
diff --git a/compose/foundation/foundation-text/lint-baseline.xml b/compose/foundation/foundation-text/lint-baseline.xml
deleted file mode 100644
index dcff3ff..0000000
--- a/compose/foundation/foundation-text/lint-baseline.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.2.0-alpha15" client="gradle" variant="debug" version="4.2.0-alpha15">
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="    return this"
-        errorLine2="           ^">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt"
-            line="630"
-            column="12"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1=") = composed {"
-        errorLine2="    ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt"
-            line="671"
-            column="5"/>
-    </issue>
-
-</issues>
diff --git a/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt b/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
index c572b80..977e99c 100644
--- a/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
+++ b/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
@@ -65,6 +65,7 @@
 import androidx.compose.ui.text.subSequence
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.util.annotation.VisibleForTesting
 import androidx.compose.ui.util.fastForEach
 import kotlin.math.floor
@@ -286,7 +287,7 @@
                             maxHeight = floor(it.height).toInt()
                         )
                     ),
-                    Offset(it.left, it.top)
+                    IntOffset(it.left.roundToInt(), it.top.roundToInt())
                 )
             }
         }
diff --git a/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt b/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
index 16d719e..d4c105b 100644
--- a/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
+++ b/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
@@ -663,6 +663,7 @@
 private val DefaultCursorThickness = 2.dp
 
 @OptIn(InternalTextApi::class)
+@Suppress("ModifierInspectorInfo")
 private fun Modifier.cursor(
     state: TextFieldState,
     value: TextFieldValue,
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index f2ecd13..db4d7902 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -59,13 +59,13 @@
   }
 
   public final class IconKt {
+    method @Deprecated @androidx.compose.runtime.Composable public static void Icon-1Wn-iBs(androidx.compose.ui.graphics.ImageBitmap bitmap, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @Deprecated @androidx.compose.runtime.Composable public static void Icon-1eoVtfs(androidx.compose.ui.graphics.painter.Painter painter, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @Deprecated @androidx.compose.runtime.Composable public static void Icon-QSAJrGw(androidx.compose.ui.graphics.vector.VectorAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
-    method @Deprecated @androidx.compose.runtime.Composable public static void Icon-uUQrhdE(androidx.compose.ui.graphics.ImageAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
   public final class ImageKt {
-    method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.ImageAsset asset, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
+    method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.ImageBitmap bitmap, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
     method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.vector.VectorAsset asset, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
     method @androidx.compose.runtime.Composable public static void Image(androidx.compose.ui.graphics.painter.Painter painter, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
   }
@@ -142,6 +142,7 @@
     method public float getMaxValue();
     method public float getValue();
     method public boolean isAnimationRunning();
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void scrollBy(float value);
     method public void scrollTo(float value);
     method public void smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.jvm.functions.Function2<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,kotlin.Unit> onEnd);
@@ -248,10 +249,16 @@
     method public suspend Object? tryAwaitRelease(kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
   }
 
+  public interface ScrollScope {
+    method public float scrollBy(float pixels);
+  }
+
   public final class ScrollableController {
     ctor public ScrollableController(internal kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> consumeScrollDelta, internal androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock, internal androidx.compose.foundation.InteractionState? interactionState);
     method public boolean isAnimationRunning();
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.jvm.functions.Function2<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,kotlin.Unit> onEnd);
+    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
     method public void stopAnimation();
     property public final boolean isAnimationRunning;
   }
@@ -323,6 +330,9 @@
     method public int getFirstVisibleItemIndex();
     method public int getFirstVisibleItemScrollOffset();
     method public boolean isAnimationRunning();
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
+    method public suspend Object? snapToItemIndex(@IntRange(from=0) int index, optional @IntRange(from=0) int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     property public final int firstVisibleItemIndex;
     property public final int firstVisibleItemScrollOffset;
     property public final boolean isAnimationRunning;
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index f2ecd13..db4d7902 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -59,13 +59,13 @@
   }
 
   public final class IconKt {
+    method @Deprecated @androidx.compose.runtime.Composable public static void Icon-1Wn-iBs(androidx.compose.ui.graphics.ImageBitmap bitmap, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @Deprecated @androidx.compose.runtime.Composable public static void Icon-1eoVtfs(androidx.compose.ui.graphics.painter.Painter painter, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @Deprecated @androidx.compose.runtime.Composable public static void Icon-QSAJrGw(androidx.compose.ui.graphics.vector.VectorAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
-    method @Deprecated @androidx.compose.runtime.Composable public static void Icon-uUQrhdE(androidx.compose.ui.graphics.ImageAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
   public final class ImageKt {
-    method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.ImageAsset asset, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
+    method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.ImageBitmap bitmap, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
     method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.vector.VectorAsset asset, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
     method @androidx.compose.runtime.Composable public static void Image(androidx.compose.ui.graphics.painter.Painter painter, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
   }
@@ -142,6 +142,7 @@
     method public float getMaxValue();
     method public float getValue();
     method public boolean isAnimationRunning();
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void scrollBy(float value);
     method public void scrollTo(float value);
     method public void smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.jvm.functions.Function2<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,kotlin.Unit> onEnd);
@@ -248,10 +249,16 @@
     method public suspend Object? tryAwaitRelease(kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
   }
 
+  public interface ScrollScope {
+    method public float scrollBy(float pixels);
+  }
+
   public final class ScrollableController {
     ctor public ScrollableController(internal kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> consumeScrollDelta, internal androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock, internal androidx.compose.foundation.InteractionState? interactionState);
     method public boolean isAnimationRunning();
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.jvm.functions.Function2<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,kotlin.Unit> onEnd);
+    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
     method public void stopAnimation();
     property public final boolean isAnimationRunning;
   }
@@ -323,6 +330,9 @@
     method public int getFirstVisibleItemIndex();
     method public int getFirstVisibleItemScrollOffset();
     method public boolean isAnimationRunning();
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
+    method public suspend Object? snapToItemIndex(@IntRange(from=0) int index, optional @IntRange(from=0) int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     property public final int firstVisibleItemIndex;
     property public final int firstVisibleItemScrollOffset;
     property public final boolean isAnimationRunning;
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index f2ecd13..db4d7902 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -59,13 +59,13 @@
   }
 
   public final class IconKt {
+    method @Deprecated @androidx.compose.runtime.Composable public static void Icon-1Wn-iBs(androidx.compose.ui.graphics.ImageBitmap bitmap, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @Deprecated @androidx.compose.runtime.Composable public static void Icon-1eoVtfs(androidx.compose.ui.graphics.painter.Painter painter, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @Deprecated @androidx.compose.runtime.Composable public static void Icon-QSAJrGw(androidx.compose.ui.graphics.vector.VectorAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
-    method @Deprecated @androidx.compose.runtime.Composable public static void Icon-uUQrhdE(androidx.compose.ui.graphics.ImageAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
   public final class ImageKt {
-    method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.ImageAsset asset, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
+    method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.ImageBitmap bitmap, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
     method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.vector.VectorAsset asset, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
     method @androidx.compose.runtime.Composable public static void Image(androidx.compose.ui.graphics.painter.Painter painter, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
   }
@@ -142,6 +142,7 @@
     method public float getMaxValue();
     method public float getValue();
     method public boolean isAnimationRunning();
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void scrollBy(float value);
     method public void scrollTo(float value);
     method public void smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.jvm.functions.Function2<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,kotlin.Unit> onEnd);
@@ -248,10 +249,16 @@
     method public suspend Object? tryAwaitRelease(kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
   }
 
+  public interface ScrollScope {
+    method public float scrollBy(float pixels);
+  }
+
   public final class ScrollableController {
     ctor public ScrollableController(internal kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> consumeScrollDelta, internal androidx.compose.foundation.animation.FlingConfig flingConfig, androidx.compose.animation.core.AnimationClockObservable animationClock, internal androidx.compose.foundation.InteractionState? interactionState);
     method public boolean isAnimationRunning();
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public void smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.jvm.functions.Function2<? super androidx.compose.animation.core.AnimationEndReason,? super java.lang.Float,kotlin.Unit> onEnd);
+    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
     method public void stopAnimation();
     property public final boolean isAnimationRunning;
   }
@@ -323,6 +330,9 @@
     method public int getFirstVisibleItemIndex();
     method public int getFirstVisibleItemScrollOffset();
     method public boolean isAnimationRunning();
+    method public suspend Object? scroll(kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method public suspend Object? smoothScrollBy(float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> spec, optional kotlin.coroutines.Continuation<? super java.lang.Float> p);
+    method public suspend Object? snapToItemIndex(@IntRange(from=0) int index, optional @IntRange(from=0) int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     property public final int firstVisibleItemIndex;
     property public final int firstVisibleItemScrollOffset;
     property public final boolean isAnimationRunning;
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
index 49c6f1e..8c7e262 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
@@ -23,6 +23,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ExperimentalLayout
+import androidx.compose.foundation.layout.FlowRow
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxHeight
@@ -47,15 +48,19 @@
 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.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.platform.LayoutDirectionAmbient
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.paging.compose.demos.PagingDemos
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.launch
 import kotlin.random.Random
 
 val LazyListDemos = listOf(
@@ -113,13 +118,62 @@
 private fun ListHoistedStateDemo() {
     val interactionState = remember { InteractionState() }
     val state = rememberLazyListState(interactionState = interactionState)
+    var lastScrollDescription: String by remember { mutableStateOf("") }
     Column {
-        Text(
-            "First item: ${state.firstVisibleItemIndex}",
-            fontSize = 30.sp
-        )
-        Text("Dragging: ${interactionState.contains(Interaction.Dragged)}", fontSize = 30.sp)
-        Text("Flinging: ${state.isAnimationRunning}", fontSize = 30.sp)
+        FlowRow {
+            val buttonModifier = Modifier.padding(8.dp)
+            val density = DensityAmbient.current
+            val coroutineScope = rememberCoroutineScope()
+            Button(
+                modifier = buttonModifier,
+                onClick = {
+                    coroutineScope.launch {
+                        state.snapToItemIndex(state.firstVisibleItemIndex - 1)
+                    }
+                }
+            ) {
+                Text("Previous")
+            }
+            Button(
+                modifier = buttonModifier,
+                onClick = {
+                    coroutineScope.launch {
+                        state.snapToItemIndex(state.firstVisibleItemIndex + 1)
+                    }
+                }
+            ) {
+                Text("Next")
+            }
+            Button(
+                modifier = buttonModifier,
+                onClick = {
+                    with(density) {
+                        coroutineScope.launch {
+                            val requestedScroll = 3000.dp.toPx()
+                            lastScrollDescription = try {
+                                val actualScroll = state.smoothScrollBy(requestedScroll)
+                                "$actualScroll/$requestedScroll px"
+                            } catch (_: CancellationException) {
+                                "Interrupted!"
+                            }
+                        }
+                    }
+                }
+            ) {
+                Text("Scroll")
+            }
+        }
+        Column {
+            Text(
+                "First item: ${state.firstVisibleItemIndex}, Last scroll: $lastScrollDescription",
+                fontSize = 20.sp
+            )
+            Text(
+                "Dragging: ${interactionState.contains(Interaction.Dragged)}, " +
+                    "Flinging: ${state.isAnimationRunning}",
+                fontSize = 20.sp
+            )
+        }
         LazyColumnFor(
             (0..1000).toList(),
             Modifier.fillMaxWidth(),
@@ -237,7 +291,10 @@
 
         val items = listOf(Color.Cyan, Color.Blue, Color.Magenta)
         itemsIndexed(items) { index, item ->
-            Box(modifier = Modifier.background(item).size(40.dp), alignment = Alignment.Center) {
+            Box(
+                modifier = Modifier.background(item).size(40.dp),
+                alignment = Alignment.Center
+            ) {
                 Text("$index", fontSize = 30.sp)
             }
         }
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/ImageSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/ImageSamples.kt
index 038f28f..d3cf9e1 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/ImageSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/ImageSamples.kt
@@ -27,7 +27,7 @@
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.graphics.painter.ImagePainter
@@ -41,19 +41,19 @@
 @Sampled
 @Composable
 fun ImageSample() {
-    val imageAsset = createTestImage()
-    // Lays out and draws an image sized to the dimensions of the ImageAsset
-    Image(asset = imageAsset)
+    val ImageBitmap = createTestImage()
+    // Lays out and draws an image sized to the dimensions of the ImageBitmap
+    Image(bitmap = ImageBitmap)
 }
 
 @Sampled
 @Composable
 fun ImagePainterSubsectionSample() {
-    val imageAsset = createTestImage()
-    // Lays out and draws an image sized to the rectangular subsection of the ImageAsset
+    val ImageBitmap = createTestImage()
+    // Lays out and draws an image sized to the rectangular subsection of the ImageBitmap
     Image(
         painter = ImagePainter(
-            imageAsset,
+            ImageBitmap,
             IntOffset(10, 12),
             IntSize(50, 60)
         )
@@ -93,13 +93,13 @@
 }
 
 /**
- * Helper method to create an ImageAsset with some content in it
+ * Helper method to create an ImageBitmap with some content in it
  */
-private fun createTestImage(): ImageAsset {
-    val imageAsset = ImageAsset(100, 100)
-    Canvas(imageAsset).drawCircle(
+private fun createTestImage(): ImageBitmap {
+    val ImageBitmap = ImageBitmap(100, 100)
+    Canvas(ImageBitmap).drawCircle(
         Offset(50.0f, 50.0f), 50.0f,
         Paint().apply { this.color = Color.Cyan }
     )
-    return imageAsset
+    return ImageBitmap
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ImageTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ImageTest.kt
index 06ce451..96f64f1 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ImageTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ImageTest.kt
@@ -31,7 +31,7 @@
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.asAndroidBitmap
@@ -78,8 +78,8 @@
     @get:Rule
     val rule = createComposeRule()
 
-    private fun createImageAsset(): ImageAsset {
-        val image = ImageAsset(imageWidth, imageHeight)
+    private fun createImageBitmap(): ImageBitmap {
+        val image = ImageBitmap(imageWidth, imageHeight)
         val path = Path().apply {
             lineTo(imageWidth.toFloat(), imageHeight.toFloat())
             lineTo(0.0f, imageHeight.toFloat())
@@ -108,7 +108,7 @@
                     .background(color = Color.White)
                     .wrapContentSize(Alignment.Center)
             ) {
-                Image(modifier = Modifier.testTag(contentTag), asset = createImageAsset())
+                Image(modifier = Modifier.testTag(contentTag), bitmap = createImageBitmap())
             }
         }
 
@@ -151,7 +151,7 @@
             ) {
                 Image(
                     ImagePainter(
-                        createImageAsset(),
+                        createImageBitmap(),
                         IntOffset(
                             imageWidth / 2 - subsectionWidth / 2,
                             imageHeight / 2 - subsectionHeight / 2
@@ -249,10 +249,10 @@
                     .wrapContentSize(Alignment.Center)
             ) {
                 // The resultant Image composable should be twice the size of the underlying
-                // ImageAsset that is to be drawn and will stretch the content to fit
+                // ImageBitmap that is to be drawn and will stretch the content to fit
                 // the bounds
                 Image(
-                    asset = createImageAsset(),
+                    bitmap = createImageBitmap(),
                     modifier = Modifier
                         .testTag(contentTag)
                         .preferredSize(
@@ -303,11 +303,11 @@
         rule.setContent {
             val density = DensityAmbient.current
             val size = (containerSize * 2 / density.density).dp
-            val imageAsset = ImageAsset(imageWidth, imageHeight)
+            val ImageBitmap = ImageBitmap(imageWidth, imageHeight)
             CanvasDrawScope().draw(
                 density,
                 LayoutDirection.Ltr,
-                Canvas(imageAsset),
+                Canvas(ImageBitmap),
                 Size(imageWidth.toFloat(), imageHeight.toFloat())
             ) {
                 drawRect(color = Color.Blue)
@@ -318,7 +318,7 @@
                     .wrapContentSize(Alignment.Center)
             ) {
                 Image(
-                    asset = imageAsset,
+                    bitmap = ImageBitmap,
                     modifier = Modifier
                         .testTag(contentTag)
                         .preferredSize(
@@ -348,16 +348,16 @@
                     .wrapContentSize(Alignment.Center)
             ) {
                 // The resultant Image composable should be twice the size of the underlying
-                // ImageAsset that is to be drawn in the bottom end section of the composable
+                // ImageBitmap that is to be drawn in the bottom end section of the composable
                 Image(
-                    asset = createImageAsset(),
+                    bitmap = createImageBitmap(),
                     modifier = Modifier
                         .testTag(contentTag)
                         .preferredSize(
                             (imageComposableWidth / density).dp,
                             (imageComposableHeight / density).dp
                         ),
-                    // Intentionally do not scale up the contents of the ImageAsset
+                    // Intentionally do not scale up the contents of the ImageBitmap
                     contentScale = ContentScale.Inside,
                     alignment = Alignment.BottomEnd
                 )
@@ -485,7 +485,7 @@
     fun testContentScaleCropRespectsMaxDimension() {
         val testTag = "testTag"
         rule.setContent {
-            val asset = with(ImageAsset(100, 100)) {
+            val asset = with(ImageBitmap(100, 100)) {
                 with(Canvas(this)) {
                     val paint = Paint().apply { this.color = Color.Blue }
                     drawRect(0f, 0f, 100f, 100f, paint)
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
index d180291..125537a 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
@@ -37,7 +37,6 @@
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.ExperimentalTesting
-import androidx.compose.ui.test.TestUiDispatcher
 import androidx.compose.ui.test.center
 import androidx.compose.ui.test.down
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -55,7 +54,9 @@
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
-import kotlinx.coroutines.withContext
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -106,7 +107,7 @@
                 duration = 100.milliseconds
             )
         }
-        advanceClockAndAwaitAnimation(clock)
+        advanceClockWhileAwaitersExist(clock)
 
         val lastTotal = rule.runOnIdle {
             assertThat(total).isGreaterThan(0)
@@ -119,7 +120,7 @@
                 duration = 100.milliseconds
             )
         }
-        advanceClockAndAwaitAnimation(clock)
+        advanceClockWhileAwaitersExist(clock)
 
         rule.runOnIdle {
             assertThat(total).isEqualTo(lastTotal)
@@ -131,7 +132,7 @@
                 duration = 100.milliseconds
             )
         }
-        advanceClockAndAwaitAnimation(clock)
+        advanceClockWhileAwaitersExist(clock)
         rule.runOnIdle {
             assertThat(total).isLessThan(0.01f)
         }
@@ -162,7 +163,7 @@
                 duration = 100.milliseconds
             )
         }
-        advanceClockAndAwaitAnimation(clock)
+        advanceClockWhileAwaitersExist(clock)
 
         val lastTotal = rule.runOnIdle {
             assertThat(total).isGreaterThan(0)
@@ -175,7 +176,7 @@
                 duration = 100.milliseconds
             )
         }
-        advanceClockAndAwaitAnimation(clock)
+        advanceClockWhileAwaitersExist(clock)
 
         rule.runOnIdle {
             assertThat(total).isEqualTo(lastTotal)
@@ -187,7 +188,7 @@
                 duration = 100.milliseconds
             )
         }
-        advanceClockAndAwaitAnimation(clock)
+        advanceClockWhileAwaitersExist(clock)
         rule.runOnIdle {
             assertThat(total).isLessThan(0.01f)
         }
@@ -231,7 +232,7 @@
             assertThat(startTrigger).isEqualTo(1)
             assertThat(stopTrigger).isEqualTo(0)
         }
-        advanceClockAndAwaitAnimation(clock)
+        advanceClockWhileAwaitersExist(clock)
         // after wait we expect stop to trigger
         rule.runOnIdle {
             assertThat(startTrigger).isEqualTo(1)
@@ -266,7 +267,7 @@
                 duration = 100.milliseconds
             )
         }
-        advanceClockAndAwaitAnimation(clock)
+        advanceClockWhileAwaitersExist(clock)
         val prevTotal = rule.runOnIdle {
             assertThat(total).isGreaterThan(0f)
             enabled.value = false
@@ -279,7 +280,7 @@
                 duration = 100.milliseconds
             )
         }
-        advanceClockAndAwaitAnimation(clock)
+        advanceClockWhileAwaitersExist(clock)
         rule.runOnIdle {
             assertThat(total).isEqualTo(prevTotal)
         }
@@ -379,7 +380,7 @@
 
     @Test
     @OptIn(ExperimentalTesting::class)
-    fun scrollable_cancel_callsDragStop() = runBlockingWithManualClock { clock ->
+    fun scrollable_cancel_callsDragStop() = runBlocking {
         var total by mutableStateOf(0f)
         var dragStopped = 0f
         val controller = ScrollableController(
@@ -388,7 +389,7 @@
                 it
             },
             flingConfig = FlingConfig(decayAnimation = ExponentialDecay()),
-            animationClock = monotonicFrameAnimationClockOf(coroutineContext, clock)
+            animationClock = monotonicFrameAnimationClockOf(coroutineContext)
         )
         setScrollableContent {
             if (total < 20) {
@@ -418,7 +419,7 @@
 
     @Test
     @OptIn(ExperimentalTesting::class)
-    fun scrollable_snappingScrolling() = runBlockingWithManualClock(true) { clock ->
+    fun scrollable_snappingScrolling() = runBlocking {
         var total = 0f
         val controller = ScrollableController(
             consumeScrollDelta = {
@@ -426,33 +427,24 @@
                 it
             },
             flingConfig = FlingConfig(decayAnimation = ExponentialDecay()),
-            animationClock = monotonicFrameAnimationClockOf(coroutineContext, clock)
+            animationClock = monotonicFrameAnimationClockOf(coroutineContext)
         )
         setScrollableContent {
             Modifier.scrollable(orientation = Orientation.Vertical, controller = controller)
         }
-        rule.runOnIdle {
-            assertThat(total).isEqualTo(0f)
-        }
-        rule.runOnIdle {
-            controller.smoothScrollBy(1000f)
-        }
-        advanceClockAndAwaitAnimation(clock)
-        rule.runOnIdle {
-            assertThat(total).isEqualTo(1000f)
-        }
-        rule.runOnIdle {
-            controller.smoothScrollBy(-200f)
-        }
-        advanceClockAndAwaitAnimation(clock)
-        rule.runOnIdle {
-            assertThat(total).isEqualTo(800f)
-        }
+        rule.awaitIdle()
+        assertThat(total).isEqualTo(0f)
+
+        controller.smoothScrollBy(1000f)
+        assertThat(total).isWithin(0.001f).of(1000f)
+
+        controller.smoothScrollBy(-200f)
+        assertThat(total).isWithin(0.001f).of(800f)
     }
 
     @Test
     @OptIn(ExperimentalTesting::class)
-    fun scrollable_explicitDisposal() = runBlockingWithManualClock(true) { clock ->
+    fun scrollable_explicitDisposal() = runBlockingWithManualClock { clock ->
         val disposed = mutableStateOf(false)
         var total = 0f
         val controller = ScrollableController(
@@ -471,25 +463,24 @@
                 Modifier
             }
         }
-        rule.runOnIdle {
+        launch {
             controller.smoothScrollBy(300f)
         }
-        advanceClockAndAwaitAnimation(clock)
-        rule.runOnIdle {
-            assertThat(total).isEqualTo(300f)
-        }
-        rule.runOnIdle {
+        advanceClockWhileAwaitersExist(clock)
+        assertThat(total).isEqualTo(300f)
+
+        launch {
             controller.smoothScrollBy(200f)
         }
         // don't advance clocks yet, toggle disposed value
-        rule.runOnUiThread {
-            disposed.value = true
-        }
-        advanceClockAndAwaitAnimation(clock)
+        disposed.value = true
+
+        // Modifier should now have been disposed and cancelled the scroll, advance clocks to
+        // confirm that it does not animate (checked in consumeScrollDelta)
+        advanceClockWhileAwaitersExist(clock)
+
         // still 300 and didn't fail in onScrollConsumptionRequested.. lambda
-        rule.runOnIdle {
-            assertThat(total).isEqualTo(300f)
-        }
+        assertThat(total).isEqualTo(300f)
     }
 
     @Test
@@ -550,8 +541,8 @@
             assertThat(outerDrag).isEqualTo(innerDrag)
             innerDrag
         }
-        advanceClockAndAwaitAnimation(clock)
-        advanceClockAndAwaitAnimation(clock)
+        advanceClockWhileAwaitersExist(clock)
+        advanceClockWhileAwaitersExist(clock)
         // and nothing should change as we don't do nested fling
         rule.runOnIdle {
             assertThat(outerDrag).isEqualTo(lastEqualDrag)
@@ -560,7 +551,7 @@
 
     @Test
     @OptIn(ExperimentalTesting::class)
-    fun scrollable_interactionState() = runBlockingWithManualClock { clock ->
+    fun scrollable_interactionState() = runBlocking {
         val interactionState = InteractionState()
         var total = 0f
         val controller = ScrollableController(
@@ -569,7 +560,7 @@
                 it
             },
             flingConfig = FlingConfig(decayAnimation = ExponentialDecay()),
-            animationClock = monotonicFrameAnimationClockOf(coroutineContext, clock),
+            animationClock = monotonicFrameAnimationClockOf(coroutineContext),
             interactionState = interactionState
         )
 
@@ -606,7 +597,7 @@
 
     @Test
     @OptIn(ExperimentalTesting::class)
-    fun scrollable_interactionState_resetWhenDisposed() = runBlockingWithManualClock { clock ->
+    fun scrollable_interactionState_resetWhenDisposed() = runBlocking {
         val interactionState = InteractionState()
         var emitScrollableBox by mutableStateOf(true)
         var total = 0f
@@ -616,7 +607,7 @@
                 it
             },
             flingConfig = FlingConfig(decayAnimation = ExponentialDecay()),
-            animationClock = monotonicFrameAnimationClockOf(coroutineContext, clock),
+            animationClock = monotonicFrameAnimationClockOf(coroutineContext),
             interactionState = interactionState
         )
 
@@ -697,10 +688,13 @@
     }
 
     @ExperimentalTesting
-    private suspend fun advanceClockAndAwaitAnimation(clock: ManualFrameClock) {
-        rule.waitForIdle()
-        withContext(TestUiDispatcher.Main) {
+    private suspend fun advanceClockWhileAwaitersExist(clock: ManualFrameClock) {
+        rule.awaitIdle()
+        yield()
+        while (clock.hasAwaiters) {
             clock.advanceClockMillis(5000L)
+            // Give awaiters the chance to await again
+            yield()
         }
     }
 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt
index 929634e..dba6330 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt
@@ -31,7 +31,7 @@
 import androidx.compose.ui.focus.isFocused
 import androidx.compose.ui.focusObserver
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.hasInputMethodsSupport
@@ -254,7 +254,7 @@
         }
     }
 
-    private fun ImageAsset.assertCursor(cursorWidth: Dp, density: Density) {
+    private fun ImageBitmap.assertCursor(cursorWidth: Dp, density: Density) {
         val cursorWidthPx = (with(density) { cursorWidth.toIntPx() })
         val width = width
         val height = height
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt
new file mode 100644
index 0000000..2f40c7c
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.foundation.layout.preferredWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
+import androidx.compose.ui.test.ExperimentalTesting
+import androidx.compose.ui.test.TestUiDispatcher
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.MediumTest
+import androidx.ui.test.createComposeRule
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import kotlin.math.roundToInt
+
+@MediumTest
+@OptIn(ExperimentalTesting::class)
+@RunWith(Parameterized::class)
+class LazyScrollTest(private val orientation: Orientation) {
+    private val LazyListTag = "LazyListTag"
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val vertical: Boolean
+        get() = orientation == Orientation.Vertical
+
+    private val items = (1..20).toList()
+    private lateinit var state: LazyListState
+
+    @Before
+    fun setup() {
+        rule.setContent {
+            state = rememberLazyListState()
+            TestContent()
+        }
+    }
+
+    @Test
+    fun testSetupWorks() {
+        assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+    }
+
+    @Test
+    fun snapToItemTest() = runBlocking {
+        withContext(TestUiDispatcher.Main) {
+            state.snapToItemIndex(3)
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(3)
+        assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+    }
+
+    @ExperimentalFoundationApi
+    @Test
+    fun smoothScrollByTest() = runBlocking {
+        withContext(TestUiDispatcher.Main) {
+            state.smoothScrollBy(with(rule.density) { 320.dp.toPx() })
+        }
+        assertThat(state.firstVisibleItemIndex).isEqualTo(3)
+        assertThat(state.firstVisibleItemScrollOffset)
+            .isEqualTo(with(rule.density) { 17.dp.toPx().roundToInt() })
+    }
+
+    @Composable
+    private fun TestContent() {
+        if (vertical) {
+            LazyColumnFor(items, Modifier.preferredHeight(300.dp), state) {
+                ItemContent()
+            }
+        } else {
+            LazyRowFor(items, Modifier.preferredWidth(300.dp), state) {
+                ItemContent()
+            }
+        }
+    }
+
+    @Composable
+    private fun LazyItemScope.ItemContent() {
+        val modifier = if (vertical) {
+            Modifier.preferredHeight(101.dp)
+        } else {
+            Modifier.preferredWidth(101.dp)
+        }
+        Spacer(modifier)
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun params() = arrayOf(Orientation.Vertical, Orientation.Horizontal)
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Icon.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Icon.kt
index 59b24a9..632b4c3 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Icon.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Icon.kt
@@ -27,7 +27,7 @@
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.painter.ImagePainter
 import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.graphics.vector.VectorAsset
@@ -61,11 +61,11 @@
 }
 
 /**
- * Icon component that draws [asset] using [tint], defaulting to [AmbientContentColor].
+ * Icon component that draws [bitmap] using [tint], defaulting to [AmbientContentColor].
  *
- * @param asset [ImageAsset] to draw inside this Icon
+ * @param bitmap [ImageBitmap] to draw inside this Icon
  * @param modifier optional [Modifier] for this Icon
- * @param tint tint to be applied to [asset]. If [Color.Unspecified] is provided, then no
+ * @param tint tint to be applied to [bitmap]. If [Color.Unspecified] is provided, then no
  *  tint is applied
  */
 @Deprecated(
@@ -74,11 +74,11 @@
 )
 @Composable
 fun Icon(
-    asset: ImageAsset,
+    bitmap: ImageBitmap,
     modifier: Modifier = Modifier,
     tint: Color = AmbientContentColor.current
 ) {
-    val painter = remember(asset) { ImagePainter(asset) }
+    val painter = remember(bitmap) { ImagePainter(bitmap) }
     @Suppress("DEPRECATION")
     Icon(
         painter = painter,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt
index 0dfd90e..1750ac9e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt
@@ -26,7 +26,7 @@
 import androidx.compose.ui.draw.paint
 import androidx.compose.ui.graphics.ColorFilter
 import androidx.compose.ui.graphics.DefaultAlpha
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.painter.ColorPainter
 import androidx.compose.ui.graphics.painter.ImagePainter
 import androidx.compose.ui.graphics.painter.Painter
@@ -35,42 +35,42 @@
 import androidx.compose.ui.layout.ContentScale
 
 /**
- * A composable that lays out and draws a given [ImageAsset]. This will attempt to
- * size the composable according to the [ImageAsset]'s given width and height. However, an
+ * A composable that lays out and draws a given [ImageBitmap]. This will attempt to
+ * size the composable according to the [ImageBitmap]'s given width and height. However, an
  * optional [Modifier] parameter can be provided to adjust sizing or draw additional content (ex.
- * background). Any unspecified dimension will leverage the [ImageAsset]'s size as a minimum
+ * background). Any unspecified dimension will leverage the [ImageBitmap]'s size as a minimum
  * constraint.
  *
  * The following sample shows basic usage of an Image composable to position and draw an
- * [ImageAsset] on screen
+ * [ImageBitmap] on screen
  * @sample androidx.compose.foundation.samples.ImageSample
  *
- * For use cases that require drawing a rectangular subset of the [ImageAsset] consumers can use
+ * For use cases that require drawing a rectangular subset of the [ImageBitmap] consumers can use
  * overload that consumes a [Painter] parameter shown in this sample
  * @sample androidx.compose.foundation.samples.ImagePainterSubsectionSample
  *
- * @param asset The [ImageAsset] to draw.
+ * @param bitmap The [ImageBitmap] to draw.
  * @param modifier Modifier used to adjust the layout algorithm or draw decoration content (ex.
  * background)
- * @param alignment Optional alignment parameter used to place the [ImageAsset] in the given
+ * @param alignment Optional alignment parameter used to place the [ImageBitmap] in the given
  * bounds defined by the width and height.
  * @param contentScale Optional scale parameter used to determine the aspect ratio scaling to be used
- * if the bounds are a different size from the intrinsic size of the [ImageAsset].
- * @param alpha Optional opacity to be applied to the [ImageAsset] when it is rendered onscreen
- * @param colorFilter Optional ColorFilter to apply for the [ImageAsset] when it is rendered
+ * if the bounds are a different size from the intrinsic size of the [ImageBitmap].
+ * @param alpha Optional opacity to be applied to the [ImageBitmap] when it is rendered onscreen
+ * @param colorFilter Optional ColorFilter to apply for the [ImageBitmap] when it is rendered
  * onscreen
  */
 @Suppress("NOTHING_TO_INLINE")
 @Composable
 inline fun Image(
-    asset: ImageAsset,
+    bitmap: ImageBitmap,
     modifier: Modifier = Modifier,
     alignment: Alignment = Alignment.Center,
     contentScale: ContentScale = ContentScale.Fit,
     alpha: Float = DefaultAlpha,
     colorFilter: ColorFilter? = null
 ) {
-    val imagePainter = remember(asset) { ImagePainter(asset) }
+    val imagePainter = remember(bitmap) { ImagePainter(bitmap) }
     Image(
         painter = imagePainter,
         modifier = modifier,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
index 5c689ed..4ba5a84 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
@@ -24,13 +24,14 @@
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.foundation.animation.FlingConfig
 import androidx.compose.foundation.animation.defaultFlingConfig
+import androidx.compose.foundation.gestures.ScrollScope
 import androidx.compose.foundation.gestures.ScrollableController
 import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
-import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.InternalLayoutApi
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.padding
@@ -44,21 +45,21 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.structuralEqualityPolicy
 import androidx.compose.ui.Alignment
-import androidx.compose.ui.layout.LayoutModifier
-import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.draw.clipToBounds
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
+import androidx.compose.ui.layout.LayoutModifier
+import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.platform.AnimationClockAmbient
 import androidx.compose.ui.platform.LayoutDirectionAmbient
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.semantics.AccessibilityScrollState
+import androidx.compose.ui.semantics.horizontalAccessibilityScrollState
 import androidx.compose.ui.semantics.scrollBy
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.semantics.horizontalAccessibilityScrollState
 import androidx.compose.ui.semantics.verticalAccessibilityScrollState
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.LayoutDirection
@@ -159,6 +160,22 @@
         )
 
     /**
+     * Call this function to take control of scrolling and gain the ability to send scroll events
+     * via [ScrollScope.scrollBy]. All actions that change the logical scroll position must be
+     * 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.
+     */
+    @OptIn(ExperimentalFoundationApi::class)
+    suspend fun scroll(
+        block: suspend ScrollScope.() -> Unit
+    ): Unit = scrollableController.scroll(block)
+
+    /**
      * Stop any ongoing animation, smooth scrolling or fling occurring on this [ScrollState]
      */
     fun stopAnimation() {
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 11506ee..f08153c 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
@@ -24,12 +24,17 @@
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Interaction
 import androidx.compose.foundation.InteractionState
 import androidx.compose.foundation.animation.FlingConfig
 import androidx.compose.foundation.animation.defaultFlingConfig
 import androidx.compose.foundation.animation.fling
+import androidx.compose.runtime.AtomicReference
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.dispatch.withFrameMillis
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.onDispose
 import androidx.compose.runtime.remember
@@ -41,6 +46,10 @@
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.platform.AnimationClockAmbient
 import androidx.compose.ui.platform.debugInspectorInfo
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
 
 /**
  * Create and remember [ScrollableController] for [scrollable] with default [FlingConfig] and
@@ -66,6 +75,18 @@
 }
 
 /**
+ * Scope used for suspending scroll blocks
+ */
+interface ScrollScope {
+    /**
+     * Attempts to scroll forward by [pixels] px.
+     *
+     * @return the amount of the requested scroll that was consumed (that is, how far it scrolled)
+     */
+    fun scrollBy(pixels: Float): Float
+}
+
+/**
  * Controller to control the [scrollable] modifier with. Contains necessary information about the
  * ongoing fling and provides smooth scrolling capabilities.
  *
@@ -100,6 +121,90 @@
         animatedFloat.animateTo(to, anim = spec, onEnd = onEnd)
     }
 
+    /**
+     * Smooth scroll by [value] pixels.
+     *
+     * Cancels the currently running scroll, if any, and suspends until the cancellation is
+     * complete.
+     *
+     * @param value delta to scroll by
+     * @param spec [AnimationSpec] to be used for this smooth scrolling
+     *
+     * @return the amount of scroll consumed
+     */
+    @OptIn(ExperimentalFoundationApi::class)
+    suspend fun smoothScrollBy(
+        value: Float,
+        spec: AnimationSpec<Float> = spring()
+    ): Float {
+        val animSpec = spec.vectorize(Float.VectorConverter)
+        val conv = Float.VectorConverter
+        val zeroVector = conv.convertToVector(0f)
+        val targetVector = conv.convertToVector(value)
+        var previousValue = 0f
+
+        scroll {
+            val startTimeMillis = withFrameMillis { it }
+            do {
+                val finished = withFrameMillis { frameTimeMillis ->
+                    val newValue = conv.convertFromVector(
+                        animSpec.getValue(
+                            playTime = frameTimeMillis - startTimeMillis,
+                            start = zeroVector,
+                            end = targetVector,
+                            // TODO: figure out if/how we should incorporate existing velocity
+                            startVelocity = zeroVector
+                        )
+                    )
+                    val delta = newValue - previousValue
+                    val consumed = scrollBy(delta)
+
+                    if (consumed != delta) {
+                        previousValue += consumed
+                        true
+                    } else {
+                        previousValue = newValue
+                        previousValue == value
+                    }
+                }
+            } while (!finished)
+        }
+        return previousValue
+    }
+
+    private val scrollControlJob = AtomicReference<Job?>(null)
+    private val scrollControlMutex = Mutex()
+
+    private val scrollScope: ScrollScope = object : ScrollScope {
+        override fun scrollBy(pixels: Float): Float = consumeScrollDelta(pixels)
+    }
+
+    /**
+     * Call this function to take control of scrolling and gain the ability to send scroll events
+     * via [ScrollScope.scrollBy]. All actions that change the logical scroll position must be
+     * 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.
+     */
+    suspend fun scroll(
+        block: suspend ScrollScope.() -> Unit
+    ): Unit = coroutineScope {
+        stopFlingAnimation()
+        val currentJob = coroutineContext[Job]
+        scrollControlJob.getAndSet(currentJob)?.cancel()
+        scrollControlMutex.withLock(currentJob) {
+            // TODO: this is a workaround to make isAnimationRunning work for now by considering all
+            //  suspend scrolls to be animations
+            isAnimationRunningState.value = true
+            scrollScope.block()
+            isAnimationRunningState.value = false
+        }
+    }
+
     private val isAnimationRunningState = mutableStateOf(false)
 
     private val clocksProxy: AnimationClockObservable = object : AnimationClockObservable {
@@ -115,20 +220,43 @@
     }
 
     /**
-     * whether this [ScrollableController] is currently animating/flinging
+     * whether this [ScrollableController] is currently scrolling via [scroll].
+     *
+     * Note: **all** scrolls initiated via [scroll] are considered to be animations, regardless of
+     * whether they are actually performing an animation.  For instance, gestures that perform
+     * scrolls via `scroll
      */
     val isAnimationRunning
         get() = isAnimationRunningState.value
 
     /**
+     * The current velocity of the fling animation.
+     *
+     * Useful for handoff between animations
+     */
+    internal val velocity: Float
+        get() = animatedFloat.velocity
+
+    /**
      * Stop any ongoing animation, smooth scrolling or fling
      *
      * Call this to stop receiving scrollable deltas in [consumeScrollDelta]
      */
-    fun stopAnimation() {
+    internal fun stopFlingAnimation() {
         animatedFloat.stop()
     }
 
+    /**
+     * Stop any ongoing animation, smooth scrolling, fling, or any other scroll occurring via
+     * [scroll].
+     *
+     * Call this to stop receiving scrollable deltas in [consumeScrollDelta]
+     */
+    fun stopAnimation() {
+        stopFlingAnimation()
+        scrollControlJob.getAndSet(null)?.cancel()
+    }
+
     private val animatedFloat =
         DeltaAnimatedFloat(0f, clocksProxy, consumeScrollDelta)
 
@@ -196,7 +324,7 @@
 
             override fun onStart(downPosition: Offset) {
                 if (enabled) {
-                    controller.stopAnimation()
+                    controller.stopFlingAnimation()
                     controller.interactionState?.addInteraction(Interaction.Dragged)
                     onScrollStarted(downPosition)
                 }
@@ -204,7 +332,7 @@
 
             override fun onScroll(scrollDistance: Float): Float {
                 if (!enabled) return 0f
-                controller.stopAnimation()
+                controller.stopFlingAnimation()
                 val toConsume = if (reverseDirection) scrollDistance * -1 else scrollDistance
                 val consumed = controller.consumeScrollDelta(toConsume)
                 controller.value = controller.value + consumed
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
index db0e9e1..e3ebd07 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
@@ -194,8 +194,7 @@
         contentPadding = contentPadding,
         verticalAlignment = verticalAlignment,
         isVertical = false
-    ) {
-        index ->
+    ) { index ->
         scope.contentFor(index, this)
     }
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
index 9c78a7a..31d7c21 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
@@ -49,7 +49,8 @@
         modifier
             .scrollable(
                 orientation = if (isVertical) Orientation.Vertical else Orientation.Horizontal,
-                reverseDirection = reverseDirection,
+                // reverse scroll by default, to have "natural" gesture that goes reversed to layout
+                reverseDirection = !reverseDirection,
                 controller = state.scrollableController
             )
             .clipToBounds()
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 c42dcb8..842c0e1 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
@@ -18,11 +18,15 @@
 
 import androidx.compose.animation.asDisposableClock
 import androidx.compose.animation.core.AnimationClockObservable
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Interaction
 import androidx.compose.foundation.InteractionState
 import androidx.compose.foundation.animation.FlingConfig
 import androidx.compose.foundation.animation.defaultFlingConfig
 import androidx.compose.foundation.assertNotNestingScrollableContainers
+import androidx.compose.foundation.gestures.ScrollScope
 import androidx.compose.foundation.gestures.ScrollableController
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
@@ -137,7 +141,11 @@
     val firstVisibleItemScrollOffset: Int get() = scrollPosition.observableScrollOffset
 
     /**
-     * whether the Lazy list with this state is currently animating/flinging
+     * whether this [LazyListState] is currently scrolling via [scroll] or via an
+     * animation/fling.
+     *
+     * Note: **all** scrolls initiated via [scroll] are considered to be animations, regardless of
+     * whether they are actually performing an animation.
      */
     val isAnimationRunning
         get() = scrollableController.isAnimationRunning
@@ -156,7 +164,7 @@
         ScrollableController(
             flingConfig = flingConfig,
             animationClock = animationClock,
-            consumeScrollDelta = { onScroll(it) },
+            consumeScrollDelta = { -onScroll(-it) },
             interactionState = interactionState
         )
 
@@ -181,21 +189,64 @@
         }
     }
 
-    // currently used by the desktop for scrollbars. to be made public
-    internal suspend fun snapToItemIndex(
-        @IntRange(from = 0) index: Int,
-        @IntRange(from = 0) scrollOffset: Int = 0
-    ) {
+    /**
+     * 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
+     * @param scrollOffset the number of pixels past the start of the item to snap to
+     */
+    @OptIn(ExperimentalFoundationApi::class)
+    suspend fun snapToItemIndex(
+        @IntRange(from = 0)
+        index: Int,
+        @IntRange(from = 0)
+        scrollOffset: Int = 0
+    ) = scrollableController.scroll {
         scrollPosition.update(
             index = DataIndex(index),
-            // scrollOffset can only be positive
-            scrollOffset = maxOf(scrollOffset, 0),
+            scrollOffset = scrollOffset,
             // `true` will be replaced with the real value during the forceRemeasure() execution
             canScrollForward = true
         )
         remeasurement.forceRemeasure()
     }
 
+    /**
+     * Call this function to take control of scrolling and gain the ability to send scroll events
+     * via [ScrollScope.scrollBy]. All actions that change the logical scroll position must be
+     * 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.
+     */
+    @OptIn(ExperimentalFoundationApi::class)
+    suspend fun scroll(
+        block: suspend ScrollScope.() -> Unit
+    ): Unit = scrollableController.scroll(block)
+
+    /**
+     * Smooth scroll by [value] pixels.
+     *
+     * Cancels the currently running scroll, if any, and suspends until the cancellation is
+     * complete.
+     *
+     * @param value delta to scroll by
+     * @param spec [AnimationSpec] to be used for this smooth scrolling
+     *
+     * @return the amount of scroll consumed
+     */
+    suspend fun smoothScrollBy(
+        value: Float,
+        spec: AnimationSpec<Float> = spring()
+    ): Float = scrollableController.smoothScrollBy(value, spec)
+
     // TODO: Coroutine scrolling APIs will allow this to be private again once we have more
     //  fine-grained control over scrolling
     @VisibleForTesting
@@ -227,7 +278,7 @@
             // We did not consume all of it - return the rest to be consumed elsewhere (e.g.,
             // nested scrolling)
             scrollToBeConsumed = 0f // We're not consuming the rest, give it back
-            scrollableController.stopAnimation()
+            scrollableController.stopFlingAnimation()
             return scrollConsumed
         }
     }
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/text/ParagraphBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/text/ParagraphBenchmark.kt
index bd15771..8f82350 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/text/ParagraphBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/text/ParagraphBenchmark.kt
@@ -25,7 +25,7 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.graphics.Canvas
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.ui.integration.test.Alphabet
 import androidx.ui.integration.test.RandomTextGenerator
 import androidx.ui.integration.test.TextBenchmarkTestRule
@@ -184,7 +184,7 @@
                     val (text, style) = text(textGenerator)
                     val paragraph = paragraph(text, style, width)
                     val canvas = Canvas(
-                        ImageAsset(paragraph.width.roundToInt(), paragraph.height.roundToInt())
+                        ImageBitmap(paragraph.width.roundToInt(), paragraph.height.roundToInt())
                     )
                     Pair(paragraph, canvas)
                 }
@@ -204,7 +204,7 @@
             // some line breaking in the result
             val paragraph = paragraph(text, style, width)
             val canvas = Canvas(
-                ImageAsset(paragraph.width.roundToInt(), paragraph.height.roundToInt())
+                ImageBitmap(paragraph.width.roundToInt(), paragraph.height.roundToInt())
             )
             // Paint for the first time, so that we only benchmark repaint.
             paragraph.paint(canvas)
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/text/TextDelegateBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/text/TextDelegateBenchmark.kt
index f948f8a..45ecf16 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/text/TextDelegateBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/text/TextDelegateBenchmark.kt
@@ -27,7 +27,7 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Paint
 import androidx.ui.integration.test.RandomTextGenerator
 import androidx.ui.integration.test.TextBenchmarkTestRule
@@ -176,7 +176,7 @@
                             layoutDirection
                         )
                         val canvas = Canvas(
-                            ImageAsset(
+                            ImageBitmap(
                                 layoutResult.size.width,
                                 layoutResult.size.height
                             )
@@ -201,7 +201,7 @@
                 layoutDirection
             )
             val canvas = Canvas(
-                ImageAsset(layoutResult.size.width, layoutResult.size.height)
+                ImageBitmap(layoutResult.size.width, layoutResult.size.height)
             )
 
             benchmarkRule.measureRepeated {
diff --git a/compose/integration-tests/macro-benchmark/build.gradle b/compose/integration-tests/macro-benchmark/build.gradle
new file mode 100644
index 0000000..fd91ff0
--- /dev/null
+++ b/compose/integration-tests/macro-benchmark/build.gradle
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 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.
+ */
+
+
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+}
+
+android.defaultConfig.minSdkVersion 28
+
+dependencies {
+    androidTestImplementation(project(":benchmark:benchmark-macro-runtime"))
+    androidTestImplementation(ANDROIDX_TEST_RULES)
+    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(ANDROIDX_TEST_CORE)
+    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+}
+
+// Define a task dependency so the app is installed before we run macro benchmarks.
+tasks.getByPath(':compose:integration-tests:macro-benchmark:connectedCheck')
+    .dependsOn(tasks.getByPath(':compose:integration-tests:demos:installRelease'))
diff --git a/compose/integration-tests/macro-benchmark/src/androidTest/AndroidManifest.xml b/compose/integration-tests/macro-benchmark/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..2c1e747
--- /dev/null
+++ b/compose/integration-tests/macro-benchmark/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.ui.macrobenchmark.test">
+
+    <!--
+    The Macro Benchmark Sample needs to launch activities in
+    `androidx.compose.integration.demos` APK.
+
+    The Macro Benchmark Library uses `PackageManager` to query for activities. This requires
+     the test APK to declare that `androidx.compose.integration.demos` be visible to
+     the APK (given Android 11's package visibility rules).
+    -->
+    <queries>
+        <package android:name="androidx.compose.integration.demos" />
+    </queries>
+</manifest>
diff --git a/compose/integration-tests/macro-benchmark/src/androidTest/java/androidx/ui/macrobenchmark/ProcessSpeedProfileValidation.kt b/compose/integration-tests/macro-benchmark/src/androidTest/java/androidx/ui/macrobenchmark/ProcessSpeedProfileValidation.kt
new file mode 100644
index 0000000..1fcb722
--- /dev/null
+++ b/compose/integration-tests/macro-benchmark/src/androidTest/java/androidx/ui/macrobenchmark/ProcessSpeedProfileValidation.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.ui.macrobenchmark
+
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.CpuUsageMetric
+import androidx.benchmark.macro.MacrobenchmarkConfig
+import androidx.benchmark.macro.MacrobenchmarkRule
+import androidx.benchmark.macro.StartupTimingMetric
+import androidx.test.filters.LargeTest
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Macrobenchmark used for local validation of performance numbers coming from MacrobenchmarkRule.
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+class ProcessSpeedProfileValidation(
+    private val compilationMode: CompilationMode,
+    private val killProcess: Boolean
+) {
+    @get:Rule
+    val benchmarkRule = MacrobenchmarkRule()
+
+    @Test
+    @Ignore("Not running the test in CI")
+    fun start() {
+        val config = MacrobenchmarkConfig(
+            packageName = PACKAGE_NAME,
+            metrics = listOf(CpuUsageMetric(), StartupTimingMetric()),
+            compilationMode = compilationMode,
+            killProcessEachIteration = killProcess,
+            iterations = 10
+        )
+        benchmarkRule.measureRepeated(config) {
+            pressHome()
+            launchPackageAndWait()
+        }
+    }
+
+    companion object {
+        private const val PACKAGE_NAME = "androidx.compose.integration.demos"
+
+        @Parameterized.Parameters(name = "compilation_mode={0}, kill_process={1}")
+        @JvmStatic
+        fun kilProcessParameters(): List<Array<Any>> {
+            val compilationModes = listOf(
+                CompilationMode.None,
+                CompilationMode.SpeedProfile(warmupIterations = 3)
+            )
+            val processKillOptions = listOf(true, false)
+            return compilationModes.zip(processKillOptions).map {
+                arrayOf(it.first, it.second)
+            }
+        }
+    }
+}
diff --git a/compose/integration-tests/macro-benchmark/src/androidTest/java/androidx/ui/macrobenchmark/StartupDemosMacrobenchmark.kt b/compose/integration-tests/macro-benchmark/src/androidTest/java/androidx/ui/macrobenchmark/StartupDemosMacrobenchmark.kt
new file mode 100644
index 0000000..f0c611b
--- /dev/null
+++ b/compose/integration-tests/macro-benchmark/src/androidTest/java/androidx/ui/macrobenchmark/StartupDemosMacrobenchmark.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.ui.macrobenchmark
+
+import androidx.benchmark.macro.MacrobenchmarkRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class StartupDemosMacrobenchmark {
+    @get:Rule
+    val benchmarkRule = MacrobenchmarkRule()
+
+    @Ignore("Not running the test in CI")
+    @Test
+    fun compiledColdStartup() = benchmarkRule.measureStartup(
+        profileCompiled = true,
+        coldLaunch = true
+    )
+
+    @Ignore("Not running the test in CI")
+    @Test
+    fun uncompiledColdStartup() = benchmarkRule.measureStartup(
+        profileCompiled = false,
+        coldLaunch = true
+    )
+}
diff --git a/compose/integration-tests/macro-benchmark/src/androidTest/java/androidx/ui/macrobenchmark/StartupUtils.kt b/compose/integration-tests/macro-benchmark/src/androidTest/java/androidx/ui/macrobenchmark/StartupUtils.kt
new file mode 100644
index 0000000..3bc5433
--- /dev/null
+++ b/compose/integration-tests/macro-benchmark/src/androidTest/java/androidx/ui/macrobenchmark/StartupUtils.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.ui.macrobenchmark
+
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.MacrobenchmarkConfig
+import androidx.benchmark.macro.MacrobenchmarkRule
+import androidx.benchmark.macro.MacrobenchmarkScope
+import androidx.benchmark.macro.StartupTimingMetric
+
+/**
+ * Simplified interface for standardizing e.g. package,
+ * compilation types, and iteration count across project
+ */
+fun MacrobenchmarkRule.measureStartup(
+    profileCompiled: Boolean,
+    coldLaunch: Boolean,
+    block: MacrobenchmarkScope.() -> Unit = {
+        pressHome()
+        launchPackageAndWait()
+    }
+) = measureRepeated(
+    MacrobenchmarkConfig(
+        packageName = "androidx.compose.integration.demos",
+        metrics = listOf(StartupTimingMetric()),
+        compilationMode = if (profileCompiled) {
+            CompilationMode.SpeedProfile(warmupIterations = 3)
+        } else {
+            CompilationMode.None
+        },
+        killProcessEachIteration = coldLaunch,
+        iterations = 10
+    ),
+    block
+)
diff --git a/compose/integration-tests/macro-benchmark/src/main/AndroidManifest.xml b/compose/integration-tests/macro-benchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..36da356
--- /dev/null
+++ b/compose/integration-tests/macro-benchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 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.
+  -->
+<manifest package="androidx.ui.macrobenchmark" />
diff --git a/compose/internal-lint-checks/src/main/java/androidx/compose/lint/ModifierInspectorInfoDetector.kt b/compose/internal-lint-checks/src/main/java/androidx/compose/lint/ModifierInspectorInfoDetector.kt
index f8b50a0..e385acc 100644
--- a/compose/internal-lint-checks/src/main/java/androidx/compose/lint/ModifierInspectorInfoDetector.kt
+++ b/compose/internal-lint-checks/src/main/java/androidx/compose/lint/ModifierInspectorInfoDetector.kt
@@ -56,7 +56,6 @@
 import org.jetbrains.uast.USwitchClauseExpression
 import org.jetbrains.uast.USwitchClauseExpressionWithBody
 import org.jetbrains.uast.USwitchExpression
-import org.jetbrains.uast.UThisExpression
 import org.jetbrains.uast.UYieldExpression
 import org.jetbrains.uast.kotlin.KotlinStringTemplateUPolyadicExpression
 import org.jetbrains.uast.kotlin.KotlinStringULiteralExpression
@@ -79,6 +78,7 @@
 private const val RememberMethodName = "remember"
 private const val ComposedMethodPackage = "androidx.compose.ui"
 private const val RememberMethodPackage = "androidx.compose.runtime"
+private val DemosPackageRegEx = "androidx\\.compose\\..+\\.demos\\..+".toRegex()
 
 /**
  * Lint [Detector] to ensure that we are creating debug information for the layout inspector on
@@ -148,7 +148,8 @@
             if (node.containingFile?.name == ModifierFile ||
                 node.containingFile?.name == ComposedModifierFile ||
                 firstParameterType(node) != ModifierClass ||
-                node.returnType?.canonicalText != ModifierClass
+                node.returnType?.canonicalText != ModifierClass ||
+                DemosPackageRegEx.matches(node.containingClass?.qualifiedName ?: "")
             ) {
                 // Ignore the method if it isn't a method on Modifier returning a Modifier,
                 // or if the method is defined in Modifier.kt or ComposedModifier.kt
@@ -430,13 +431,12 @@
          *
          * The expression is known to be the return expression from a return statement.
          *
-         * Currently the only accepted expressions are of the form:
+         * Currently the only check the following expressions:
          * - Modifier.then(Modifier)
          * - Modifier.composed(InspectorInfoLambda,factory)
-         * - synonymCall()
-         * - everything else is an error
+         * - everything else is ignored
          */
-        private inner class ModifierBuilderVisitor : UnexpectedVisitor({ wrongLambda(it) }) {
+        private inner class ModifierBuilderVisitor : AbstractUastVisitor() {
             override fun visitQualifiedReferenceExpression(
                 node: UQualifiedReferenceExpression
             ): Boolean {
@@ -453,24 +453,15 @@
                 if (isComposeFunctionCall(node)) {
                     val inspectorInfo = node.valueArguments
                         .find { isInspectorInfoLambdaType(it.getExpressionType()) }
-                        ?: return super.visitCallExpression(node)
-                    inspectorInfo.accept(debugInspectorVisitor)
+                    if (inspectorInfo == null) {
+                        wrongLambda(node)
+                    } else {
+                        inspectorInfo.accept(debugInspectorVisitor)
+                    }
                     return true
                 }
-                // For now accept all other calls. Assume that the method being called
-                // will add inspector information.
-                return true
+                return super.visitCallExpression(node)
             }
-
-            override fun visitSimpleNameReferenceExpression(
-                node: USimpleNameReferenceExpression
-            ): Boolean {
-                // Accept a simple reference to a different modifier definition
-                return false
-            }
-
-            // Accept a single this expression, which essentially makes the modifier a noop
-            override fun visitThisExpression(node: UThisExpression): Boolean = false
         }
 
         /**
diff --git a/compose/internal-lint-checks/src/test/java/androidx/compose/lint/ModifierInspectorInfoDetectorTest.kt b/compose/internal-lint-checks/src/test/java/androidx/compose/lint/ModifierInspectorInfoDetectorTest.kt
index 906edb9..1bc855c3 100644
--- a/compose/internal-lint-checks/src/test/java/androidx/compose/lint/ModifierInspectorInfoDetectorTest.kt
+++ b/compose/internal-lint-checks/src/test/java/androidx/compose/lint/ModifierInspectorInfoDetectorTest.kt
@@ -428,6 +428,9 @@
                     .then(if (bottom > 0) padding(bottom) else Modifier)
                     .then(if (top > 0) padding(top) else Modifier)
 
+                fun Modifier.paddingFromBaseline2(top: Int, bottom: Int) =
+                    this.padding(bottom).padding(top)
+
                 private class PaddingModifier(
                     paddingSize: Int,
                     inspectorInfo: InspectorInfo.() -> Unit
@@ -514,6 +517,22 @@
                     }
                 )
 
+                fun Modifier.border2(width: Int, color: Int, shape: Shape): Modifier =
+                    if (width > 0) {
+                        composed(
+                            inspectorInfo = debugInspectorInfo {
+                                name = "border2"
+                                properties["width"] = width
+                                properties["color"] = color
+                                properties["shape"] = shape
+                            }
+                        ) {
+                            border(width, SolidColor(color), shape)
+                        }
+                    } else {
+                        this
+                    }
+
                 private class BorderModifier(shape: Shape, width: Int, brush: Brush)
 
                 """
@@ -620,6 +639,33 @@
     }
 
     @Test
+    fun acceptMissingInspectorInfoInSamples() {
+        lint().files(
+            modifierFile,
+            inspectableInfoFile,
+            kotlin(
+                """
+                package androidx.compose.ui.demos.whatever
+
+                import androidx.compose.ui.Modifier
+                import androidx.compose.ui.platform.InspectorInfo
+                import androidx.compose.ui.platform.InspectorValueInfo
+                import androidx.compose.ui.platform.debugInspectorInfo
+
+                fun Modifier.preferredWidth2(width: Int) = this.then(SizeModifier2(width))
+
+                private data class SizeModifier2(
+                    val width: Int,
+                ): Modifier.Element
+
+                """
+            ).indented()
+        )
+            .run()
+            .expectClean()
+    }
+
+    @Test
     fun missingInspectorInfo() {
         lint().files(
             modifierFile,
@@ -1097,4 +1143,75 @@
                 """
             )
     }
+
+    @Test
+    fun missingInfoInConditionals() {
+        lint().files(
+            modifierFile,
+            inspectableInfoFile,
+            kotlin(
+                """
+                package androidx.compose.ui
+
+                import androidx.compose.ui.Modifier
+                import androidx.compose.ui.platform.InspectorInfo
+                import androidx.compose.ui.platform.InspectorValueInfo
+                import androidx.compose.ui.platform.debugInspectorInfo
+
+                class Brush
+
+                class SolidColor(val color: Int): Brush()
+
+                fun Modifier.border(width: Int): Modifier = composed(
+                    inspectorInfo = debugInspectorInfo {
+                        name = "border"
+                        value = width
+                    }
+                ) {
+                    BorderModifier(shape, width, brush)
+                }
+
+                fun Modifier.border2(width: Int): Modifier =
+                    if (width > 0) {
+                        border(width)
+                    } else {
+                        composed { BorderModifier(shape, width, brush) }
+                    }
+
+                fun Modifier.border3(width: Int): Modifier =
+                    when {
+                        width < 0 -> this
+                        width < 2 -> border(width)
+                        width < 3 -> composed { BorderModifier(shape, width, brush) }
+                        else -> this
+                    }
+
+                fun Modifier.border4(width: Int): Modifier =
+                    when {
+                        width < 0 -> this
+                        width < 2 -> border(width)
+                        else -> this.then(BorderModifier(shape, width, brush))
+                    }
+
+                private class BorderModifier(shape: Shape, width: Int, brush: Brush): Modifier
+
+                """
+            ).indented()
+        )
+            .run()
+            .expect(
+                """
+                    src/androidx/compose/ui/Brush.kt:25: Error: Modifier missing inspectorInfo [ModifierInspectorInfo]
+                            composed { BorderModifier(shape, width, brush) }
+                            ~~~~~~~~
+                    src/androidx/compose/ui/Brush.kt:32: Error: Modifier missing inspectorInfo [ModifierInspectorInfo]
+                            width < 3 -> composed { BorderModifier(shape, width, brush) }
+                                         ~~~~~~~~
+                    src/androidx/compose/ui/Brush.kt:40: Error: Modifier missing inspectorInfo [ModifierInspectorInfo]
+                            else -> this.then(BorderModifier(shape, width, brush))
+                                              ~~~~~~~~~~~~~~
+                    3 errors, 0 warnings
+                """
+            )
+    }
 }
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index e472fbe..9bd3ea4b 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -382,9 +382,9 @@
   }
 
   public final class IconKt {
+    method @androidx.compose.runtime.Composable public static void Icon-1Wn-iBs(androidx.compose.ui.graphics.ImageBitmap bitmap, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon-1eoVtfs(androidx.compose.ui.graphics.painter.Painter painter, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon-QSAJrGw(androidx.compose.ui.graphics.vector.VectorAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
-    method @androidx.compose.runtime.Composable public static void Icon-uUQrhdE(androidx.compose.ui.graphics.ImageAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
   public final class ListItemKt {
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index e472fbe..9bd3ea4b 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -382,9 +382,9 @@
   }
 
   public final class IconKt {
+    method @androidx.compose.runtime.Composable public static void Icon-1Wn-iBs(androidx.compose.ui.graphics.ImageBitmap bitmap, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon-1eoVtfs(androidx.compose.ui.graphics.painter.Painter painter, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon-QSAJrGw(androidx.compose.ui.graphics.vector.VectorAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
-    method @androidx.compose.runtime.Composable public static void Icon-uUQrhdE(androidx.compose.ui.graphics.ImageAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
   public final class ListItemKt {
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index e472fbe..9bd3ea4b 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -382,9 +382,9 @@
   }
 
   public final class IconKt {
+    method @androidx.compose.runtime.Composable public static void Icon-1Wn-iBs(androidx.compose.ui.graphics.ImageBitmap bitmap, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon-1eoVtfs(androidx.compose.ui.graphics.painter.Painter painter, optional androidx.compose.ui.Modifier modifier, optional long tint);
     method @androidx.compose.runtime.Composable public static void Icon-QSAJrGw(androidx.compose.ui.graphics.vector.VectorAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
-    method @androidx.compose.runtime.Composable public static void Icon-uUQrhdE(androidx.compose.ui.graphics.ImageAsset asset, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
   public final class ListItemKt {
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
index 6d1a995..22e72b7 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
@@ -59,7 +59,7 @@
 import androidx.compose.ui.gesture.dragGestureFilter
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.isSpecified
@@ -115,7 +115,7 @@
         )
 
         Box(Modifier.fillMaxSize()) {
-            Image(modifier = inputModifier, asset = colorWheel.image)
+            Image(modifier = inputModifier, bitmap = colorWheel.image)
             val color = colorWheel.colorForPosition(position)
             if (color.isSpecified) {
                 Magnifier(visible = isDragging, position = position, color = color)
@@ -305,7 +305,7 @@
 }
 
 /**
- * A color wheel with an [ImageAsset] that draws a circular color wheel of the specified diameter.
+ * A color wheel with an [ImageBitmap] that draws a circular color wheel of the specified diameter.
  */
 private class ColorWheel(diameter: Int) {
     private val radius = diameter / 2f
@@ -326,8 +326,8 @@
         null
     )
 
-    val image = ImageAsset(diameter, diameter).also { asset ->
-        val canvas = Canvas(asset)
+    val image = ImageBitmap(diameter, diameter).also { imageBitmap ->
+        val canvas = Canvas(imageBitmap)
         val center = Offset(radius, radius)
         val paint = Paint().apply { shader = sweepShader }
         canvas.drawCircle(center, radius, paint)
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MenuDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MenuDemo.kt
index 24de963..ddbfc47 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MenuDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MenuDemo.kt
@@ -31,8 +31,11 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.Position
 import androidx.compose.ui.unit.dp
 import kotlin.math.roundToInt
@@ -44,8 +47,12 @@
             for (j in 0..10) {
                 MenuInstance(
                     Modifier.fillMaxSize().wrapContentSize(
-                        { _, space, _ ->
-                            IntOffset(
+                        object : Alignment {
+                            override fun align(
+                                size: IntSize,
+                                space: IntSize,
+                                layoutDirection: LayoutDirection
+                            ) = IntOffset(
                                 (space.width * i / 10f).roundToInt(),
                                 (space.height * j / 10f).roundToInt()
                             )
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
index 0260073..b02eeb7 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
@@ -33,15 +33,15 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.vector.VectorAsset
 
 @Sampled
 @Composable
 fun OneLineListItems(
-    icon24x24: ImageAsset,
-    icon40x40: ImageAsset,
-    icon56x56: ImageAsset,
+    icon24x24: ImageBitmap,
+    icon40x40: ImageBitmap,
+    icon56x56: ImageBitmap,
     vectorIcon: VectorAsset
 ) {
     Column {
@@ -94,8 +94,8 @@
 
 @Sampled
 @Composable
-// TODO(popam, b/159689286): material icons instead of ImageAsset when they can have custom sizes
-fun TwoLineListItems(icon24x24: ImageAsset, icon40x40: ImageAsset) {
+// TODO(popam, b/159689286): material icons instead of ImageBitmap when they can have custom sizes
+fun TwoLineListItems(icon24x24: ImageBitmap, icon40x40: ImageBitmap) {
     Column {
         ListItem(
             text = { Text("Two line list item") },
@@ -151,7 +151,7 @@
 
 @Sampled
 @Composable
-fun ThreeLineListItems(icon24x24: ImageAsset, vectorIcon: VectorAsset) {
+fun ThreeLineListItems(icon24x24: ImageBitmap, vectorIcon: VectorAsset) {
     Column {
         ListItem(
             text = { Text("Three line list item") },
@@ -208,7 +208,7 @@
 // Demos for mixing RTL and LTR ListItems:
 
 @Composable
-fun OneLineRtlLtrListItems(icon24x24: ImageAsset, icon40x40: ImageAsset) {
+fun OneLineRtlLtrListItems(icon24x24: ImageBitmap, icon40x40: ImageBitmap) {
     Column {
         ListItem(text = { Text("One line list item with no icon") })
         Divider()
@@ -236,7 +236,7 @@
 }
 
 @Composable
-fun TwoLineRtlLtrListItems(icon40x40: ImageAsset) {
+fun TwoLineRtlLtrListItems(icon40x40: ImageBitmap) {
     Column {
         ListItem(
             text = { Text("Two line list item") },
@@ -297,7 +297,7 @@
 }
 
 @Composable
-fun ThreeLineRtlLtrListItems(icon40x40: ImageAsset) {
+fun ThreeLineRtlLtrListItems(icon40x40: ImageBitmap) {
     Column {
         ListItem(
             text = { Text("Three line list item") },
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt
index abb9316..4f02a23 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt
@@ -22,7 +22,7 @@
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
 import androidx.compose.ui.graphics.painter.ColorPainter
 import androidx.compose.ui.graphics.painter.ImagePainter
@@ -85,7 +85,7 @@
         rule
             .setMaterialContentForSizeAssertions {
                 val image = with(DensityAmbient.current) {
-                    ImageAsset(width.toIntPx(), height.toIntPx())
+                    ImageBitmap(width.toIntPx(), height.toIntPx())
                 }
 
                 Icon(image)
@@ -102,7 +102,7 @@
         rule
             .setMaterialContentForSizeAssertions {
                 val image = with(DensityAmbient.current) {
-                    ImageAsset(width.toIntPx(), height.toIntPx())
+                    ImageBitmap(width.toIntPx(), height.toIntPx())
                 }
 
                 Icon(image)
@@ -132,7 +132,7 @@
         rule
             .setMaterialContentForSizeAssertions {
                 val image = with(DensityAmbient.current) {
-                    ImageAsset(width.toIntPx(), height.toIntPx())
+                    ImageBitmap(width.toIntPx(), height.toIntPx())
                 }
 
                 val imagePainter = ImagePainter(image)
@@ -148,7 +148,7 @@
         val height = 83.dp
         val testTag = "testTag"
         rule.setMaterialContentForSizeAssertions {
-            val image: ImageAsset
+            val image: ImageBitmap
             with(DensityAmbient.current) {
                 image = createBitmapWithColor(
                     this,
@@ -170,7 +170,7 @@
         val height = 83.dp
         val testTag = "testTag"
         rule.setMaterialContentForSizeAssertions {
-            val image: ImageAsset
+            val image: ImageBitmap
             with(DensityAmbient.current) {
                 image = createBitmapWithColor(
                     this,
@@ -191,9 +191,9 @@
         width: Int,
         height: Int,
         color: Color
-    ): ImageAsset {
+    ): ImageBitmap {
         val size = Size(width.toFloat(), height.toFloat())
-        val image = ImageAsset(width, height)
+        val image = ImageBitmap(width, height)
         CanvasDrawScope().draw(
             density,
             LayoutDirection.Ltr,
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ListItemTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ListItemTest.kt
index 459d809..00034d3 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ListItemTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ListItemTest.kt
@@ -20,7 +20,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.layout.FirstBaseline
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.onGloballyPositioned
@@ -45,9 +45,9 @@
     @get:Rule
     val rule = createComposeRule()
 
-    val icon24x24 by lazy { ImageAsset(width = 24.dp.toIntPx(), height = 24.dp.toIntPx()) }
-    val icon40x40 by lazy { ImageAsset(width = 40.dp.toIntPx(), height = 40.dp.toIntPx()) }
-    val icon56x56 by lazy { ImageAsset(width = 56.dp.toIntPx(), height = 56.dp.toIntPx()) }
+    val icon24x24 by lazy { ImageBitmap(width = 24.dp.toIntPx(), height = 24.dp.toIntPx()) }
+    val icon40x40 by lazy { ImageBitmap(width = 40.dp.toIntPx(), height = 40.dp.toIntPx()) }
+    val icon56x56 by lazy { ImageBitmap(width = 56.dp.toIntPx(), height = 56.dp.toIntPx()) }
 
     @Test
     fun listItem_oneLine_size() {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt
index 105e998..6b5fbf2 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt
@@ -25,7 +25,7 @@
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.painter.ImagePainter
 import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.graphics.vector.VectorAsset
@@ -55,21 +55,21 @@
 }
 
 /**
- * Icon component that draws [asset] using [tint], defaulting to [AmbientContentColor]. For a
+ * Icon component that draws [bitmap] using [tint], defaulting to [AmbientContentColor]. For a
  * clickable icon, see [IconButton].
  *
- * @param asset [ImageAsset] to draw inside this Icon
+ * @param bitmap [ImageBitmap] to draw inside this Icon
  * @param modifier optional [Modifier] for this Icon
- * @param tint tint to be applied to [asset]. If [Color.Unspecified] is provided, then no
+ * @param tint tint to be applied to [bitmap]. If [Color.Unspecified] is provided, then no
  *  tint is applied
  */
 @Composable
 fun Icon(
-    asset: ImageAsset,
+    bitmap: ImageBitmap,
     modifier: Modifier = Modifier,
     tint: Color = AmbientContentColor.current
 ) {
-    val painter = remember(asset) { ImagePainter(asset) }
+    val painter = remember(bitmap) { ImagePainter(bitmap) }
     Icon(
         painter = painter,
         modifier = modifier,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt
index 94e17da..9dbb12d 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt
@@ -277,10 +277,17 @@
 ) {
     val colorAndEmphasis = @Composable {
         Providers(AmbientContentColor provides contentColor) {
-            if (contentAlpha != null) Providers(
-                AmbientContentAlpha provides contentAlpha,
-                children = children
-            ) else children()
+            if (contentAlpha != null) {
+                Providers(
+                    AmbientContentAlpha provides contentAlpha,
+                    children = children
+                )
+            } else {
+                Providers(
+                    AmbientContentAlpha provides contentColor.alpha,
+                    children = children
+                )
+            }
         }
     }
     if (typography != null) ProvideTextStyle(typography, colorAndEmphasis) else colorAndEmphasis()
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
index c4ece4d..15f2ae2 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
@@ -2081,15 +2081,10 @@
     private fun removeSlots(start: Int, len: Int, group: Int) {
         if (len > 0) {
             val gapLen = slotsGapLen
-            if (gapLen == 0) {
-                slotsGapStart = start
-                slotsGapLen = len
-            } else {
-                val removeEnd = start + len
-                moveSlotGapTo(removeEnd, group)
-                slotsGapStart = start
-                slotsGapLen = gapLen + len
-            }
+            val removeEnd = start + len
+            moveSlotGapTo(removeEnd, group)
+            slotsGapStart = start
+            slotsGapLen = gapLen + len
             slots.fill(null, start, start + len)
             val currentDataEnd = currentSlotEnd
             if (currentDataEnd >= start) this.currentSlotEnd = currentDataEnd - len
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt
index a561908..cf04643 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt
@@ -2834,6 +2834,54 @@
     }
 
     @Test
+    fun testRemoveDataBoundaryCondition() {
+        // Remove when the slot table contains amount that would make the slotGapSize 0
+        // Test insert exactly 64 data slots.
+        val slots = SlotTable().also {
+            it.write { writer ->
+                writer.insert {
+                    writer.group(treeRoot) {
+                        repeat(4) {
+                            writer.group(it * 10 + 100) {
+                                repeat(8) { value ->
+                                    writer.update(value)
+                                }
+                            }
+                        }
+                        writer.group(1000) {
+                            repeat(16) { value ->
+                                writer.update(value)
+                            }
+                        }
+                        repeat(2) {
+                            writer.group(it * 10 + 200) {
+                                repeat(8) { value ->
+                                    writer.update(value)
+                                }
+                            }
+                        }
+                        repeat(10) {
+                            writer.group(300 + it) { }
+                        }
+                    }
+                }
+            }
+        }
+        slots.verifyWellFormed()
+
+        slots.write { writer ->
+            writer.group(treeRoot) {
+                repeat(4) { writer.skipGroup() }
+                writer.removeGroup()
+                writer.skipGroup()
+                writer.set(4, 100)
+                writer.skipToGroupEnd()
+            }
+        }
+        slots.verifyWellFormed()
+    }
+
+    @Test
     fun testInsertDataBoundaryCondition() {
         // Test insert exactly 64 data slots.
         val slots = SlotTable().also {
diff --git a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ImageAssertions.kt b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ImageAssertions.kt
index db0e572..9df3255 100644
--- a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ImageAssertions.kt
+++ b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ImageAssertions.kt
@@ -20,12 +20,12 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.asAndroidBitmap
-import androidx.compose.ui.graphics.asImageAsset
+import androidx.compose.ui.graphics.asImageBitmap
 import androidx.compose.ui.test.assertContainsColor
 import androidx.compose.ui.test.assertPixelColor
 import androidx.compose.ui.test.assertPixels
@@ -46,7 +46,7 @@
  * @throws AssertionError if size or colors don't match.
  */
 @Suppress("DEPRECATION")
-fun ImageAsset.assertPixels(
+fun ImageBitmap.assertPixels(
     expectedSize: IntSize? = null,
     expectedColorProvider: (pos: IntOffset) -> Color?
 ) = asAndroidBitmap().assertPixels(expectedSize, expectedColorProvider)
@@ -55,7 +55,7 @@
  * Asserts that the color at a specific pixel in the bitmap at ([x], [y]) is [expected].
  */
 @Suppress("DEPRECATION")
-fun ImageAsset.assertPixelColor(
+fun ImageBitmap.assertPixelColor(
     expected: Color,
     x: Int,
     y: Int,
@@ -68,9 +68,9 @@
  * @throws AssertionError if the expected color is not present.
  */
 @Suppress("DEPRECATION")
-fun ImageAsset.assertContainsColor(
+fun ImageBitmap.assertContainsColor(
     expectedColor: Color
-) = asAndroidBitmap().assertContainsColor(expectedColor).asImageAsset()
+) = asAndroidBitmap().assertContainsColor(expectedColor).asImageBitmap()
 
 /**
  * Tests to see if the given point is within the path. (That is, whether the
@@ -106,7 +106,7 @@
  */
 // TODO (mount, malkov) : to investigate why it flakes when shape is not rect
 @Suppress("DEPRECATION")
-fun ImageAsset.assertShape(
+fun ImageBitmap.assertShape(
     density: Density,
     shape: Shape,
     shapeColor: Color,
@@ -149,7 +149,7 @@
  * untested as it is likely anti-aliased. The default is 1 pixel
  */
 @Suppress("DEPRECATION")
-fun ImageAsset.assertShape(
+fun ImageBitmap.assertShape(
     density: Density,
     horizontalPadding: Dp,
     verticalPadding: Dp,
diff --git a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ScreenshotTestsUtils.kt b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ScreenshotTestsUtils.kt
index b0da4ba4..79dc916 100644
--- a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ScreenshotTestsUtils.kt
+++ b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ScreenshotTestsUtils.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.testutils
 
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.test.screenshot.ScreenshotTestRule
 import androidx.test.screenshot.assertAgainstGolden
@@ -35,7 +35,7 @@
  * @param matcher The algorithm to be used to perform the matching. By default [MSSIMMatcher]
  * is used.
  */
-fun ImageAsset.assertAgainstGolden(
+fun ImageBitmap.assertAgainstGolden(
     rule: ScreenshotTestRule,
     goldenIdentifier: String,
     matcher: BitmapMatcher = MSSIMMatcher()
diff --git a/compose/ui/ui-graphics/api/current.txt b/compose/ui/ui-graphics/api/current.txt
index 82b07b5..97981c8 100644
--- a/compose/ui/ui-graphics/api/current.txt
+++ b/compose/ui/ui-graphics/api/current.txt
@@ -10,10 +10,11 @@
     method public static android.graphics.Canvas getNativeCanvas(androidx.compose.ui.graphics.Canvas);
   }
 
-  public final class AndroidImageAssetKt {
-    method public static android.graphics.Bitmap asAndroidBitmap(androidx.compose.ui.graphics.ImageAsset);
-    method public static androidx.compose.ui.graphics.ImageAsset asImageAsset(android.graphics.Bitmap);
-    method public static androidx.compose.ui.graphics.ImageAsset imageFromResource(android.content.res.Resources res, int resId);
+  public final class AndroidImageBitmapKt {
+    method public static android.graphics.Bitmap asAndroidBitmap(androidx.compose.ui.graphics.ImageBitmap);
+    method @Deprecated public static androidx.compose.ui.graphics.ImageBitmap asImageAsset(android.graphics.Bitmap);
+    method public static androidx.compose.ui.graphics.ImageBitmap asImageBitmap(android.graphics.Bitmap);
+    method public static androidx.compose.ui.graphics.ImageBitmap imageFromResource(android.content.res.Resources res, int resId);
   }
 
   public final class AndroidMatrixConversionsKt {
@@ -188,8 +189,8 @@
     method public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, androidx.compose.ui.graphics.Paint paint);
     method public default void drawArcRad(androidx.compose.ui.geometry.Rect rect, float startAngleRad, float sweepAngleRad, boolean useCenter, androidx.compose.ui.graphics.Paint paint);
     method public void drawCircle-tVKstsI(long center, float radius, androidx.compose.ui.graphics.Paint paint);
-    method public void drawImage-YdY9XZw(androidx.compose.ui.graphics.ImageAsset image, long topLeftOffset, androidx.compose.ui.graphics.Paint paint);
-    method public void drawImageRect-iaf9M94(androidx.compose.ui.graphics.ImageAsset image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, androidx.compose.ui.graphics.Paint paint);
+    method public void drawImage-uwcbMjI(androidx.compose.ui.graphics.ImageBitmap image, long topLeftOffset, androidx.compose.ui.graphics.Paint paint);
+    method public void drawImageRect-bgE79EM(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, androidx.compose.ui.graphics.Paint paint);
     method public void drawLine-bYPfCD8(long p1, long p2, androidx.compose.ui.graphics.Paint paint);
     method public default void drawOval(androidx.compose.ui.geometry.Rect rect, androidx.compose.ui.graphics.Paint paint);
     method public void drawOval(float left, float top, float right, float bottom, androidx.compose.ui.graphics.Paint paint);
@@ -217,7 +218,7 @@
   }
 
   public final class CanvasKt {
-    method public static androidx.compose.ui.graphics.Canvas Canvas(androidx.compose.ui.graphics.ImageAsset image);
+    method public static androidx.compose.ui.graphics.Canvas Canvas(androidx.compose.ui.graphics.ImageBitmap image);
     method public static void rotate(androidx.compose.ui.graphics.Canvas, float degrees, float pivotX, float pivotY);
     method public static void rotateRad(androidx.compose.ui.graphics.Canvas, float radians, optional float pivotX, optional float pivotY);
     method public static void scale(androidx.compose.ui.graphics.Canvas, float sx, optional float sy, float pivotX, float pivotY);
@@ -325,32 +326,33 @@
   public final class Float16Kt {
   }
 
-  public interface ImageAsset {
+  public interface ImageBitmap {
     method public androidx.compose.ui.graphics.colorspace.ColorSpace getColorSpace();
-    method public androidx.compose.ui.graphics.ImageAssetConfig getConfig();
+    method public androidx.compose.ui.graphics.ImageBitmapConfig getConfig();
     method public boolean getHasAlpha();
     method public int getHeight();
     method public int getWidth();
     method public void prepareToDraw();
     method public void readPixels(int[] buffer, optional int startX, optional int startY, optional int width, optional int height, optional int bufferOffset, optional int stride);
     property public abstract androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace;
-    property public abstract androidx.compose.ui.graphics.ImageAssetConfig config;
+    property public abstract androidx.compose.ui.graphics.ImageBitmapConfig config;
     property public abstract boolean hasAlpha;
     property public abstract int height;
     property public abstract int width;
   }
 
-  public enum ImageAssetConfig {
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Alpha8;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Argb8888;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig F16;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Gpu;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Rgb565;
+  public enum ImageBitmapConfig {
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Alpha8;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Argb8888;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig F16;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Gpu;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Rgb565;
   }
 
-  public final class ImageAssetKt {
-    method public static androidx.compose.ui.graphics.ImageAsset ImageAsset(int width, int height, optional androidx.compose.ui.graphics.ImageAssetConfig config, optional boolean hasAlpha, optional androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
-    method public static androidx.compose.ui.graphics.PixelMap toPixelMap(androidx.compose.ui.graphics.ImageAsset, optional int startX, optional int startY, optional int width, optional int height, optional int[] buffer, optional int bufferOffset, optional int stride);
+  public final class ImageBitmapKt {
+    method @Deprecated public static androidx.compose.ui.graphics.ImageBitmap ImageAsset(int width, int height, optional androidx.compose.ui.graphics.ImageBitmapConfig config, optional boolean hasAlpha, optional androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
+    method public static androidx.compose.ui.graphics.ImageBitmap ImageBitmap(int width, int height, optional androidx.compose.ui.graphics.ImageBitmapConfig config, optional boolean hasAlpha, optional androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
+    method public static androidx.compose.ui.graphics.PixelMap toPixelMap(androidx.compose.ui.graphics.ImageBitmap, optional int startX, optional int startY, optional int width, optional int height, optional int[] buffer, optional int bufferOffset, optional int stride);
   }
 
   @androidx.compose.runtime.Immutable public final class LinearGradient extends androidx.compose.ui.graphics.ShaderBrush {
@@ -571,7 +573,7 @@
   }
 
   public final class ShaderKt {
-    method public static android.graphics.Shader ImageShader(androidx.compose.ui.graphics.ImageAsset image, optional androidx.compose.ui.graphics.TileMode tileModeX, optional androidx.compose.ui.graphics.TileMode tileModeY);
+    method public static android.graphics.Shader ImageShader(androidx.compose.ui.graphics.ImageBitmap image, optional androidx.compose.ui.graphics.TileMode tileModeX, optional androidx.compose.ui.graphics.TileMode tileModeY);
     method public static android.graphics.Shader LinearGradientShader-GfyHbQM(long from, long to, java.util.List<androidx.compose.ui.graphics.Color> colors, optional java.util.List<java.lang.Float>? colorStops, optional androidx.compose.ui.graphics.TileMode tileMode);
     method public static android.graphics.Shader RadialGradientShader-cY6o93o(long center, float radius, java.util.List<androidx.compose.ui.graphics.Color> colors, optional java.util.List<java.lang.Float>? colorStops, optional androidx.compose.ui.graphics.TileMode tileMode);
   }
@@ -854,8 +856,8 @@
     method public void drawArc-C-Io9bM(androidx.compose.ui.graphics.Brush brush, float startAngle, float sweepAngle, boolean useCenter, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-NGaRamM(long color, float radius, long center, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, float radius, long center, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-WFpWo7g(androidx.compose.ui.graphics.ImageAsset image, long topLeft, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-oX8chH0(androidx.compose.ui.graphics.ImageAsset image, long srcOffset, long srcSize, long dstOffset, long dstSize, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, long topLeft, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, long srcOffset, long srcSize, long dstOffset, long dstSize, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-1-s4MmQ(long color, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
@@ -896,8 +898,8 @@
     method public void drawArc-C-Io9bM(androidx.compose.ui.graphics.Brush brush, float startAngle, float sweepAngle, boolean useCenter, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-NGaRamM(long color, optional float radius, optional long center, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, optional float radius, optional long center, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-WFpWo7g(androidx.compose.ui.graphics.ImageAsset image, optional long topLeft, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-oX8chH0(androidx.compose.ui.graphics.ImageAsset image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, optional long topLeft, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-1-s4MmQ(long color, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
@@ -1019,7 +1021,7 @@
   }
 
   public final class ImagePainter extends androidx.compose.ui.graphics.painter.Painter {
-    method public androidx.compose.ui.graphics.painter.ImagePainter copy-rHaiP50(androidx.compose.ui.graphics.ImageAsset image, long srcOffset, long srcSize);
+    method public androidx.compose.ui.graphics.painter.ImagePainter copy-MHcbwps(androidx.compose.ui.graphics.ImageBitmap image, long srcOffset, long srcSize);
     method public long getIntrinsicSize-NH-jbRc();
     method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);
     property public long intrinsicSize;
diff --git a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
index 82b07b5..97981c8 100644
--- a/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-graphics/api/public_plus_experimental_current.txt
@@ -10,10 +10,11 @@
     method public static android.graphics.Canvas getNativeCanvas(androidx.compose.ui.graphics.Canvas);
   }
 
-  public final class AndroidImageAssetKt {
-    method public static android.graphics.Bitmap asAndroidBitmap(androidx.compose.ui.graphics.ImageAsset);
-    method public static androidx.compose.ui.graphics.ImageAsset asImageAsset(android.graphics.Bitmap);
-    method public static androidx.compose.ui.graphics.ImageAsset imageFromResource(android.content.res.Resources res, int resId);
+  public final class AndroidImageBitmapKt {
+    method public static android.graphics.Bitmap asAndroidBitmap(androidx.compose.ui.graphics.ImageBitmap);
+    method @Deprecated public static androidx.compose.ui.graphics.ImageBitmap asImageAsset(android.graphics.Bitmap);
+    method public static androidx.compose.ui.graphics.ImageBitmap asImageBitmap(android.graphics.Bitmap);
+    method public static androidx.compose.ui.graphics.ImageBitmap imageFromResource(android.content.res.Resources res, int resId);
   }
 
   public final class AndroidMatrixConversionsKt {
@@ -188,8 +189,8 @@
     method public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, androidx.compose.ui.graphics.Paint paint);
     method public default void drawArcRad(androidx.compose.ui.geometry.Rect rect, float startAngleRad, float sweepAngleRad, boolean useCenter, androidx.compose.ui.graphics.Paint paint);
     method public void drawCircle-tVKstsI(long center, float radius, androidx.compose.ui.graphics.Paint paint);
-    method public void drawImage-YdY9XZw(androidx.compose.ui.graphics.ImageAsset image, long topLeftOffset, androidx.compose.ui.graphics.Paint paint);
-    method public void drawImageRect-iaf9M94(androidx.compose.ui.graphics.ImageAsset image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, androidx.compose.ui.graphics.Paint paint);
+    method public void drawImage-uwcbMjI(androidx.compose.ui.graphics.ImageBitmap image, long topLeftOffset, androidx.compose.ui.graphics.Paint paint);
+    method public void drawImageRect-bgE79EM(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, androidx.compose.ui.graphics.Paint paint);
     method public void drawLine-bYPfCD8(long p1, long p2, androidx.compose.ui.graphics.Paint paint);
     method public default void drawOval(androidx.compose.ui.geometry.Rect rect, androidx.compose.ui.graphics.Paint paint);
     method public void drawOval(float left, float top, float right, float bottom, androidx.compose.ui.graphics.Paint paint);
@@ -217,7 +218,7 @@
   }
 
   public final class CanvasKt {
-    method public static androidx.compose.ui.graphics.Canvas Canvas(androidx.compose.ui.graphics.ImageAsset image);
+    method public static androidx.compose.ui.graphics.Canvas Canvas(androidx.compose.ui.graphics.ImageBitmap image);
     method public static void rotate(androidx.compose.ui.graphics.Canvas, float degrees, float pivotX, float pivotY);
     method public static void rotateRad(androidx.compose.ui.graphics.Canvas, float radians, optional float pivotX, optional float pivotY);
     method public static void scale(androidx.compose.ui.graphics.Canvas, float sx, optional float sy, float pivotX, float pivotY);
@@ -325,32 +326,33 @@
   public final class Float16Kt {
   }
 
-  public interface ImageAsset {
+  public interface ImageBitmap {
     method public androidx.compose.ui.graphics.colorspace.ColorSpace getColorSpace();
-    method public androidx.compose.ui.graphics.ImageAssetConfig getConfig();
+    method public androidx.compose.ui.graphics.ImageBitmapConfig getConfig();
     method public boolean getHasAlpha();
     method public int getHeight();
     method public int getWidth();
     method public void prepareToDraw();
     method public void readPixels(int[] buffer, optional int startX, optional int startY, optional int width, optional int height, optional int bufferOffset, optional int stride);
     property public abstract androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace;
-    property public abstract androidx.compose.ui.graphics.ImageAssetConfig config;
+    property public abstract androidx.compose.ui.graphics.ImageBitmapConfig config;
     property public abstract boolean hasAlpha;
     property public abstract int height;
     property public abstract int width;
   }
 
-  public enum ImageAssetConfig {
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Alpha8;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Argb8888;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig F16;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Gpu;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Rgb565;
+  public enum ImageBitmapConfig {
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Alpha8;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Argb8888;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig F16;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Gpu;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Rgb565;
   }
 
-  public final class ImageAssetKt {
-    method public static androidx.compose.ui.graphics.ImageAsset ImageAsset(int width, int height, optional androidx.compose.ui.graphics.ImageAssetConfig config, optional boolean hasAlpha, optional androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
-    method public static androidx.compose.ui.graphics.PixelMap toPixelMap(androidx.compose.ui.graphics.ImageAsset, optional int startX, optional int startY, optional int width, optional int height, optional int[] buffer, optional int bufferOffset, optional int stride);
+  public final class ImageBitmapKt {
+    method @Deprecated public static androidx.compose.ui.graphics.ImageBitmap ImageAsset(int width, int height, optional androidx.compose.ui.graphics.ImageBitmapConfig config, optional boolean hasAlpha, optional androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
+    method public static androidx.compose.ui.graphics.ImageBitmap ImageBitmap(int width, int height, optional androidx.compose.ui.graphics.ImageBitmapConfig config, optional boolean hasAlpha, optional androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
+    method public static androidx.compose.ui.graphics.PixelMap toPixelMap(androidx.compose.ui.graphics.ImageBitmap, optional int startX, optional int startY, optional int width, optional int height, optional int[] buffer, optional int bufferOffset, optional int stride);
   }
 
   @androidx.compose.runtime.Immutable public final class LinearGradient extends androidx.compose.ui.graphics.ShaderBrush {
@@ -571,7 +573,7 @@
   }
 
   public final class ShaderKt {
-    method public static android.graphics.Shader ImageShader(androidx.compose.ui.graphics.ImageAsset image, optional androidx.compose.ui.graphics.TileMode tileModeX, optional androidx.compose.ui.graphics.TileMode tileModeY);
+    method public static android.graphics.Shader ImageShader(androidx.compose.ui.graphics.ImageBitmap image, optional androidx.compose.ui.graphics.TileMode tileModeX, optional androidx.compose.ui.graphics.TileMode tileModeY);
     method public static android.graphics.Shader LinearGradientShader-GfyHbQM(long from, long to, java.util.List<androidx.compose.ui.graphics.Color> colors, optional java.util.List<java.lang.Float>? colorStops, optional androidx.compose.ui.graphics.TileMode tileMode);
     method public static android.graphics.Shader RadialGradientShader-cY6o93o(long center, float radius, java.util.List<androidx.compose.ui.graphics.Color> colors, optional java.util.List<java.lang.Float>? colorStops, optional androidx.compose.ui.graphics.TileMode tileMode);
   }
@@ -854,8 +856,8 @@
     method public void drawArc-C-Io9bM(androidx.compose.ui.graphics.Brush brush, float startAngle, float sweepAngle, boolean useCenter, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-NGaRamM(long color, float radius, long center, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, float radius, long center, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-WFpWo7g(androidx.compose.ui.graphics.ImageAsset image, long topLeft, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-oX8chH0(androidx.compose.ui.graphics.ImageAsset image, long srcOffset, long srcSize, long dstOffset, long dstSize, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, long topLeft, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, long srcOffset, long srcSize, long dstOffset, long dstSize, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-1-s4MmQ(long color, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
@@ -896,8 +898,8 @@
     method public void drawArc-C-Io9bM(androidx.compose.ui.graphics.Brush brush, float startAngle, float sweepAngle, boolean useCenter, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-NGaRamM(long color, optional float radius, optional long center, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, optional float radius, optional long center, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-WFpWo7g(androidx.compose.ui.graphics.ImageAsset image, optional long topLeft, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-oX8chH0(androidx.compose.ui.graphics.ImageAsset image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, optional long topLeft, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-1-s4MmQ(long color, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
@@ -1019,7 +1021,7 @@
   }
 
   public final class ImagePainter extends androidx.compose.ui.graphics.painter.Painter {
-    method public androidx.compose.ui.graphics.painter.ImagePainter copy-rHaiP50(androidx.compose.ui.graphics.ImageAsset image, long srcOffset, long srcSize);
+    method public androidx.compose.ui.graphics.painter.ImagePainter copy-MHcbwps(androidx.compose.ui.graphics.ImageBitmap image, long srcOffset, long srcSize);
     method public long getIntrinsicSize-NH-jbRc();
     method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);
     property public long intrinsicSize;
diff --git a/compose/ui/ui-graphics/api/restricted_current.txt b/compose/ui/ui-graphics/api/restricted_current.txt
index 729264d..21672de 100644
--- a/compose/ui/ui-graphics/api/restricted_current.txt
+++ b/compose/ui/ui-graphics/api/restricted_current.txt
@@ -13,8 +13,8 @@
     method public void disableZ();
     method public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, androidx.compose.ui.graphics.Paint paint);
     method public void drawCircle-tVKstsI(long center, float radius, androidx.compose.ui.graphics.Paint paint);
-    method public void drawImage-YdY9XZw(androidx.compose.ui.graphics.ImageAsset image, long topLeftOffset, androidx.compose.ui.graphics.Paint paint);
-    method public void drawImageRect-iaf9M94(androidx.compose.ui.graphics.ImageAsset image, long srcOffset, long srcSize, long dstOffset, long dstSize, androidx.compose.ui.graphics.Paint paint);
+    method public void drawImage-uwcbMjI(androidx.compose.ui.graphics.ImageBitmap image, long topLeftOffset, androidx.compose.ui.graphics.Paint paint);
+    method public void drawImageRect-bgE79EM(androidx.compose.ui.graphics.ImageBitmap image, long srcOffset, long srcSize, long dstOffset, long dstSize, androidx.compose.ui.graphics.Paint paint);
     method public void drawLine-bYPfCD8(long p1, long p2, androidx.compose.ui.graphics.Paint paint);
     method public void drawOval(float left, float top, float right, float bottom, androidx.compose.ui.graphics.Paint paint);
     method public void drawPath(androidx.compose.ui.graphics.Path path, androidx.compose.ui.graphics.Paint paint);
@@ -40,10 +40,11 @@
     method public static android.graphics.Canvas getNativeCanvas(androidx.compose.ui.graphics.Canvas);
   }
 
-  public final class AndroidImageAssetKt {
-    method public static android.graphics.Bitmap asAndroidBitmap(androidx.compose.ui.graphics.ImageAsset);
-    method public static androidx.compose.ui.graphics.ImageAsset asImageAsset(android.graphics.Bitmap);
-    method public static androidx.compose.ui.graphics.ImageAsset imageFromResource(android.content.res.Resources res, int resId);
+  public final class AndroidImageBitmapKt {
+    method public static android.graphics.Bitmap asAndroidBitmap(androidx.compose.ui.graphics.ImageBitmap);
+    method @Deprecated public static androidx.compose.ui.graphics.ImageBitmap asImageAsset(android.graphics.Bitmap);
+    method public static androidx.compose.ui.graphics.ImageBitmap asImageBitmap(android.graphics.Bitmap);
+    method public static androidx.compose.ui.graphics.ImageBitmap imageFromResource(android.content.res.Resources res, int resId);
   }
 
   public final class AndroidMatrixConversionsKt {
@@ -218,8 +219,8 @@
     method public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, androidx.compose.ui.graphics.Paint paint);
     method public default void drawArcRad(androidx.compose.ui.geometry.Rect rect, float startAngleRad, float sweepAngleRad, boolean useCenter, androidx.compose.ui.graphics.Paint paint);
     method public void drawCircle-tVKstsI(long center, float radius, androidx.compose.ui.graphics.Paint paint);
-    method public void drawImage-YdY9XZw(androidx.compose.ui.graphics.ImageAsset image, long topLeftOffset, androidx.compose.ui.graphics.Paint paint);
-    method public void drawImageRect-iaf9M94(androidx.compose.ui.graphics.ImageAsset image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, androidx.compose.ui.graphics.Paint paint);
+    method public void drawImage-uwcbMjI(androidx.compose.ui.graphics.ImageBitmap image, long topLeftOffset, androidx.compose.ui.graphics.Paint paint);
+    method public void drawImageRect-bgE79EM(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, androidx.compose.ui.graphics.Paint paint);
     method public void drawLine-bYPfCD8(long p1, long p2, androidx.compose.ui.graphics.Paint paint);
     method public default void drawOval(androidx.compose.ui.geometry.Rect rect, androidx.compose.ui.graphics.Paint paint);
     method public void drawOval(float left, float top, float right, float bottom, androidx.compose.ui.graphics.Paint paint);
@@ -248,7 +249,7 @@
   }
 
   public final class CanvasKt {
-    method public static androidx.compose.ui.graphics.Canvas Canvas(androidx.compose.ui.graphics.ImageAsset image);
+    method public static androidx.compose.ui.graphics.Canvas Canvas(androidx.compose.ui.graphics.ImageBitmap image);
     method public static void rotate(androidx.compose.ui.graphics.Canvas, float degrees, float pivotX, float pivotY);
     method public static void rotateRad(androidx.compose.ui.graphics.Canvas, float radians, optional float pivotX, optional float pivotY);
     method public static void scale(androidx.compose.ui.graphics.Canvas, float sx, optional float sy, float pivotX, float pivotY);
@@ -357,32 +358,33 @@
   public final class Float16Kt {
   }
 
-  public interface ImageAsset {
+  public interface ImageBitmap {
     method public androidx.compose.ui.graphics.colorspace.ColorSpace getColorSpace();
-    method public androidx.compose.ui.graphics.ImageAssetConfig getConfig();
+    method public androidx.compose.ui.graphics.ImageBitmapConfig getConfig();
     method public boolean getHasAlpha();
     method public int getHeight();
     method public int getWidth();
     method public void prepareToDraw();
     method public void readPixels(int[] buffer, optional int startX, optional int startY, optional int width, optional int height, optional int bufferOffset, optional int stride);
     property public abstract androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace;
-    property public abstract androidx.compose.ui.graphics.ImageAssetConfig config;
+    property public abstract androidx.compose.ui.graphics.ImageBitmapConfig config;
     property public abstract boolean hasAlpha;
     property public abstract int height;
     property public abstract int width;
   }
 
-  public enum ImageAssetConfig {
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Alpha8;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Argb8888;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig F16;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Gpu;
-    enum_constant public static final androidx.compose.ui.graphics.ImageAssetConfig Rgb565;
+  public enum ImageBitmapConfig {
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Alpha8;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Argb8888;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig F16;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Gpu;
+    enum_constant public static final androidx.compose.ui.graphics.ImageBitmapConfig Rgb565;
   }
 
-  public final class ImageAssetKt {
-    method public static androidx.compose.ui.graphics.ImageAsset ImageAsset(int width, int height, optional androidx.compose.ui.graphics.ImageAssetConfig config, optional boolean hasAlpha, optional androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
-    method public static androidx.compose.ui.graphics.PixelMap toPixelMap(androidx.compose.ui.graphics.ImageAsset, optional int startX, optional int startY, optional int width, optional int height, optional int[] buffer, optional int bufferOffset, optional int stride);
+  public final class ImageBitmapKt {
+    method @Deprecated public static androidx.compose.ui.graphics.ImageBitmap ImageAsset(int width, int height, optional androidx.compose.ui.graphics.ImageBitmapConfig config, optional boolean hasAlpha, optional androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
+    method public static androidx.compose.ui.graphics.ImageBitmap ImageBitmap(int width, int height, optional androidx.compose.ui.graphics.ImageBitmapConfig config, optional boolean hasAlpha, optional androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
+    method public static androidx.compose.ui.graphics.PixelMap toPixelMap(androidx.compose.ui.graphics.ImageBitmap, optional int startX, optional int startY, optional int width, optional int height, optional int[] buffer, optional int bufferOffset, optional int stride);
   }
 
   @androidx.compose.runtime.Immutable public final class LinearGradient extends androidx.compose.ui.graphics.ShaderBrush {
@@ -603,7 +605,7 @@
   }
 
   public final class ShaderKt {
-    method public static android.graphics.Shader ImageShader(androidx.compose.ui.graphics.ImageAsset image, optional androidx.compose.ui.graphics.TileMode tileModeX, optional androidx.compose.ui.graphics.TileMode tileModeY);
+    method public static android.graphics.Shader ImageShader(androidx.compose.ui.graphics.ImageBitmap image, optional androidx.compose.ui.graphics.TileMode tileModeX, optional androidx.compose.ui.graphics.TileMode tileModeY);
     method public static android.graphics.Shader LinearGradientShader-GfyHbQM(long from, long to, java.util.List<androidx.compose.ui.graphics.Color> colors, optional java.util.List<java.lang.Float>? colorStops, optional androidx.compose.ui.graphics.TileMode tileMode);
     method public static android.graphics.Shader RadialGradientShader-cY6o93o(long center, float radius, java.util.List<androidx.compose.ui.graphics.Color> colors, optional java.util.List<java.lang.Float>? colorStops, optional androidx.compose.ui.graphics.TileMode tileMode);
   }
@@ -886,8 +888,8 @@
     method public void drawArc-C-Io9bM(androidx.compose.ui.graphics.Brush brush, float startAngle, float sweepAngle, boolean useCenter, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-NGaRamM(long color, float radius, long center, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, float radius, long center, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-WFpWo7g(androidx.compose.ui.graphics.ImageAsset image, long topLeft, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-oX8chH0(androidx.compose.ui.graphics.ImageAsset image, long srcOffset, long srcSize, long dstOffset, long dstSize, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, long topLeft, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, long srcOffset, long srcSize, long dstOffset, long dstSize, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-1-s4MmQ(long color, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, float strokeWidth, androidx.compose.ui.graphics.StrokeCap cap, android.graphics.PathEffect? pathEffect, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, @FloatRange(from=0.0, to=1.0) float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, androidx.compose.ui.graphics.BlendMode blendMode);
@@ -952,8 +954,8 @@
     method public void drawArc-C-Io9bM(androidx.compose.ui.graphics.Brush brush, float startAngle, float sweepAngle, boolean useCenter, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-NGaRamM(long color, optional float radius, optional long center, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawCircle-m-UMHxE(androidx.compose.ui.graphics.Brush brush, optional float radius, optional long center, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-WFpWo7g(androidx.compose.ui.graphics.ImageAsset image, optional long topLeft, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
-    method public void drawImage-oX8chH0(androidx.compose.ui.graphics.ImageAsset image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-JUiai_k(androidx.compose.ui.graphics.ImageBitmap image, optional long topLeft, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
+    method public void drawImage-Yc2aOMw(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-1-s4MmQ(long color, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawLine-IQGCKvc(androidx.compose.ui.graphics.Brush brush, long start, long end, optional float strokeWidth, optional androidx.compose.ui.graphics.StrokeCap cap, optional android.graphics.PathEffect? pathEffect, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
     method public void drawOval-0a6MmAQ(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional @FloatRange(from=0.0, to=1.0) float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional androidx.compose.ui.graphics.BlendMode blendMode);
@@ -1075,7 +1077,7 @@
   }
 
   public final class ImagePainter extends androidx.compose.ui.graphics.painter.Painter {
-    method public androidx.compose.ui.graphics.painter.ImagePainter copy-rHaiP50(androidx.compose.ui.graphics.ImageAsset image, long srcOffset, long srcSize);
+    method public androidx.compose.ui.graphics.painter.ImagePainter copy-MHcbwps(androidx.compose.ui.graphics.ImageBitmap image, long srcOffset, long srcSize);
     method public long getIntrinsicSize-NH-jbRc();
     method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);
     property public long intrinsicSize;
diff --git a/compose/ui/ui-graphics/lint-baseline.xml b/compose/ui/ui-graphics/lint-baseline.xml
index ada17b4..ac13d90 100644
--- a/compose/ui/ui-graphics/lint-baseline.xml
+++ b/compose/ui/ui-graphics/lint-baseline.xml
@@ -3,7 +3,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        bitmap = Bitmap.createBitmap("
         errorLine2="                        ~~~~~~~~~~~~">
         <location
@@ -14,7 +14,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAsset is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmap is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="            bitmap.colorSpace?.toComposeColorSpace() ?: ColorSpaces.Srgb"
         errorLine2="                   ~~~~~~~~~~">
         <location
@@ -25,7 +25,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="    return android.graphics.ColorSpace.get(frameworkNamedSpace)"
         errorLine2="                                       ~~~">
         <location
@@ -36,7 +36,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.SRGB)"
         errorLine2="                                    ~~~">
         <location
@@ -47,7 +47,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.ACES)"
         errorLine2="                                    ~~~">
         <location
@@ -58,7 +58,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.ACESCG)"
         errorLine2="                                    ~~~">
         <location
@@ -69,7 +69,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.ADOBE_RGB)"
         errorLine2="                                    ~~~">
         <location
@@ -80,7 +80,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.BT2020)"
         errorLine2="                                    ~~~">
         <location
@@ -91,7 +91,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.BT709)"
         errorLine2="                                    ~~~">
         <location
@@ -102,7 +102,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.CIE_LAB)"
         errorLine2="                                    ~~~">
         <location
@@ -113,7 +113,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.CIE_XYZ)"
         errorLine2="                                    ~~~">
         <location
@@ -124,7 +124,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.DCI_P3)"
         errorLine2="                                    ~~~">
         <location
@@ -135,7 +135,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.DISPLAY_P3)"
         errorLine2="                                    ~~~">
         <location
@@ -146,7 +146,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.EXTENDED_SRGB)"
         errorLine2="                                    ~~~">
         <location
@@ -157,7 +157,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.LINEAR_EXTENDED_SRGB)"
         errorLine2="                                    ~~~">
         <location
@@ -168,7 +168,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.LINEAR_SRGB)"
         errorLine2="                                    ~~~">
         <location
@@ -179,7 +179,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.NTSC_1953)"
         errorLine2="                                    ~~~">
         <location
@@ -190,7 +190,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.PRO_PHOTO_RGB)"
         errorLine2="                                    ~~~">
         <location
@@ -201,7 +201,7 @@
 
     <issue
         id="UnsafeNewApiCall"
-        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageAssetKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
+        message="This call is to a method from API 26, the call containing class androidx.compose.ui.graphics.AndroidImageBitmapKt is not annotated with @RequiresApi(x) where x is at least 26. Either annotate the containing class with at least @RequiresApi(26) or move the call to a static method in a wrapper class annotated with at least @RequiresApi(26)."
         errorLine1="        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.SMPTE_C)"
         errorLine2="                                    ~~~">
         <location
diff --git a/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/ImageAssetSample.kt b/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/ImageAssetSample.kt
index cb699c3..a5bd091 100644
--- a/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/ImageAssetSample.kt
+++ b/compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/ImageAssetSample.kt
@@ -20,22 +20,22 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.PixelMap
 import androidx.compose.ui.graphics.toPixelMap
 
 /**
  * Sample showing how to obtain a [PixelMap] to query pixel information
- * from an underlying [ImageAsset]
+ * from an underlying [ImageBitmap]
  */
 @Sampled
-fun ImageAssetToPixelMapSample() {
-    val imageAsset = createImageAsset()
+fun ImageBitmapToPixelMapSample() {
+    val imageBitmap = createImageBitmap()
 
-    // Sample a 3 by 2 subsection of the given ImageAsset
+    // Sample a 3 by 2 subsection of the given ImageBitmap
     // starting at the coordinate (48, 49)
-    val pixelmap = imageAsset.toPixelMap(
+    val pixelmap = imageBitmap.toPixelMap(
         startX = 48,
         startY = 49,
         width = 3,
@@ -43,7 +43,7 @@
     )
 
     // create a histogram to count the number of occurrences of a color within the specified
-    // subsection of the provided ImageAsset
+    // subsection of the provided ImageBitmap
     val histogram = HashMap<Color, Int>()
     for (x in 0 until pixelmap.width) {
         for (y in 0 until pixelmap.height) {
@@ -55,16 +55,16 @@
 }
 
 /**
- * [ImageAsset.readPixels] sample that shows how to create a consumer defined
+ * [ImageBitmap.readPixels] sample that shows how to create a consumer defined
  * IntArray to store pixel information and create a PixelMap for querying information
  * within the buffer
  */
 @Sampled
-fun ImageAssetReadPixelsSample() {
-    val imageAsset = createImageAsset()
+fun ImageBitmapReadPixelsSample() {
+    val imageBitmap = createImageBitmap()
 
     val buffer = IntArray(20 * 10)
-    imageAsset.readPixels(
+    imageBitmap.readPixels(
         buffer = buffer,
         startX = 8,
         startY = 9,
@@ -81,7 +81,7 @@
     )
 
     // create a histogram to count the number of occurrences of a color within the specified
-    // subsection of the provided ImageAsset
+    // subsection of the provided ImageBitmap
     val histogram = HashMap<Color, Int>()
     for (x in 0 until pixelmap.width) {
         for (y in 0 until pixelmap.height) {
@@ -92,9 +92,9 @@
     }
 }
 
-private fun createImageAsset(): ImageAsset {
-    val imageAsset = ImageAsset(100, 100)
-    val canvas = Canvas(imageAsset)
+private fun createImageBitmap(): ImageBitmap {
+    val imageBitmap = ImageBitmap(100, 100)
+    val canvas = Canvas(imageBitmap)
     val paint = Paint()
 
     // Draw 4 colored squares that are red, blue, green and yellow from the top left, top right
@@ -110,5 +110,5 @@
 
     paint.color = Color.Yellow
     canvas.drawRect(Rect(50.0f, 50.0f, 100.0f, 100.0f), paint)
-    return imageAsset
+    return imageBitmap
 }
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidCanvasTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidCanvasTest.kt
index 2e47f48..ecec048 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidCanvasTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidCanvasTest.kt
@@ -89,8 +89,8 @@
         val fg = androidx.compose.ui.graphics.Color.Blue
         val width = 200
         val height = 200
-        val imageAsset = ImageAsset(width, height)
-        val canvas = Canvas(imageAsset)
+        val imageBitmap = ImageBitmap(width, height)
+        val canvas = Canvas(imageBitmap)
         val paint = Paint().apply { this.color = bg }
         val rect = Rect(Offset.Zero, Size(width.toFloat(), height.toFloat()))
         with(canvas) {
@@ -102,7 +102,7 @@
             }
         }
 
-        val pixelMap = imageAsset.toPixelMap()
+        val pixelMap = imageBitmap.toPixelMap()
         assertEquals(fg, pixelMap[0, 0])
         assertEquals(fg, pixelMap[99, 0])
         assertEquals(fg, pixelMap[0, 99])
@@ -121,8 +121,8 @@
         val fg = androidx.compose.ui.graphics.Color.Blue
         val width = 200
         val height = 200
-        val imageAsset = ImageAsset(width, height)
-        val canvas = Canvas(imageAsset)
+        val imageBitmap = ImageBitmap(width, height)
+        val canvas = Canvas(imageBitmap)
         val paint = Paint().apply { this.color = bg }
         val rect = Rect(Offset(0.0f, 0.0f), Size(width.toFloat(), height.toFloat()))
         with(canvas) {
@@ -134,7 +134,7 @@
             }
         }
 
-        val pixelMap = imageAsset.toPixelMap()
+        val pixelMap = imageBitmap.toPixelMap()
         val left = width / 2 - 50
         val top = height / 2 - 50
         val right = width / 2 + 50 - 1
@@ -167,8 +167,8 @@
         val fg = androidx.compose.ui.graphics.Color.Blue
         val width = 200
         val height = 200
-        val imageAsset = ImageAsset(width, height)
-        val canvas = Canvas(imageAsset)
+        val imageBitmap = ImageBitmap(width, height)
+        val canvas = Canvas(imageBitmap)
         val paint = Paint().apply { this.color = bg }
         val rect = Rect(Offset(0.0f, 0.0f), Size(width.toFloat(), height.toFloat()))
         with(canvas) {
@@ -180,7 +180,7 @@
             }
         }
 
-        val pixelMap = imageAsset.toPixelMap()
+        val pixelMap = imageBitmap.toPixelMap()
 
         val left = width - 100
         val top = height - 100
@@ -205,8 +205,8 @@
         val fg = androidx.compose.ui.graphics.Color.Blue
         val width = 200
         val height = 200
-        val imageAsset = ImageAsset(width, height)
-        val canvas = Canvas(imageAsset)
+        val imageBitmap = ImageBitmap(width, height)
+        val canvas = Canvas(imageBitmap)
         val paint = Paint().apply { this.color = bg }
         val rect = Rect(Offset(0.0f, 0.0f), Size(width.toFloat(), height.toFloat()))
         with(canvas) {
@@ -221,7 +221,7 @@
             }
         }
 
-        val pixelMap = imageAsset.toPixelMap()
+        val pixelMap = imageBitmap.toPixelMap()
         assertEquals(fg, pixelMap[0, 0])
         assertEquals(fg, pixelMap[99, 0])
         assertEquals(fg, pixelMap[0, 99])
@@ -240,8 +240,8 @@
         val fg = androidx.compose.ui.graphics.Color.Blue
         val width = 200
         val height = 200
-        val imageAsset = ImageAsset(width, height)
-        val canvas = Canvas(imageAsset)
+        val imageBitmap = ImageBitmap(width, height)
+        val canvas = Canvas(imageBitmap)
         val paint = Paint().apply { this.color = bg }
         val rect = Rect(Offset(0.0f, 0.0f), Size(width.toFloat(), height.toFloat()))
         with(canvas) {
@@ -256,7 +256,7 @@
             }
         }
 
-        val pixelMap = imageAsset.toPixelMap()
+        val pixelMap = imageBitmap.toPixelMap()
         assertEquals(fg, pixelMap[2, 0])
         assertEquals(fg, pixelMap[50, 49])
         assertEquals(fg, pixelMap[70, 0])
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ImageAssetTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ImageBitmapTest.kt
similarity index 88%
rename from compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ImageAssetTest.kt
rename to compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ImageBitmapTest.kt
index b871b51..ab73407 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ImageAssetTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/ImageBitmapTest.kt
@@ -26,22 +26,22 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class ImageAssetTest {
+class ImageBitmapTest {
 
     @Test
     fun testCreatedImage() {
         val cs = ColorSpaces.Srgb
-        val image = ImageAsset(
+        val image = ImageBitmap(
             width = 10,
             height = 20,
-            config = ImageAssetConfig.Argb8888,
+            config = ImageBitmapConfig.Argb8888,
             hasAlpha = false,
             colorSpace = cs
         )
 
         assertEquals(10, image.width)
         assertEquals(20, image.height)
-        assertEquals(ImageAssetConfig.Argb8888, image.config)
+        assertEquals(ImageBitmapConfig.Argb8888, image.config)
         assertFalse(image.hasAlpha)
         assertEquals(cs, image.colorSpace)
     }
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt
index 6b208cc..dafe263 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathTest.kt
@@ -34,7 +34,7 @@
     fun testAddArcPath() {
         val width = 100
         val height = 100
-        val image = ImageAsset(width, height)
+        val image = ImageBitmap(width, height)
         val canvas = Canvas(image)
         val path1 = Path().apply {
             addArcRad(
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PixelMapTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PixelMapTest.kt
index fbb2f5c..a733508 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PixelMapTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PixelMapTest.kt
@@ -27,9 +27,9 @@
 @RunWith(AndroidJUnit4::class)
 class PixelMapTest {
 
-    fun createImageAsset(): ImageAsset {
-        val imageAsset = ImageAsset(100, 100)
-        val canvas = Canvas(imageAsset)
+    fun createImageBitmap(): ImageBitmap {
+        val ImageBitmap = ImageBitmap(100, 100)
+        val canvas = Canvas(ImageBitmap)
         val paint = Paint().apply { this.color = Color.Red }
 
         canvas.drawRect(Rect(0.0f, 0.0f, 50.0f, 50.0f), paint)
@@ -42,13 +42,13 @@
 
         paint.color = Color.Yellow
         canvas.drawRect(Rect(50.0f, 50.0f, 100.0f, 100.0f), paint)
-        return imageAsset
+        return ImageBitmap
     }
 
     @Test
-    fun testImageAssetPixelMap() {
-        val imageAsset = createImageAsset()
-        val pixelmap = imageAsset.toPixelMap()
+    fun testImageBitmapPixelMap() {
+        val ImageBitmap = createImageBitmap()
+        val pixelmap = ImageBitmap.toPixelMap()
 
         Assert.assertEquals(Color.Red, pixelmap[0, 0])
         Assert.assertEquals(Color.Red, pixelmap[49, 0])
@@ -72,8 +72,8 @@
     }
 
     @Test
-    fun testImageAssetSubsection() {
-        val asset = createImageAsset()
+    fun testImageBitmapSubsection() {
+        val asset = createImageBitmap()
         val subsectionWidth = 3
         val subsectionHeight = 2
         val bufferOffset = 3
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeTest.kt
index c098d4f..b78b5f0 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeTest.kt
@@ -22,7 +22,7 @@
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.ClipOp
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.PointMode
@@ -49,8 +49,8 @@
     private val height: Int = 100
     private val dstSize = Size(width.toFloat(), height.toFloat())
 
-    private fun createTestDstImage(): ImageAsset {
-        val dst = ImageAsset(width, height)
+    private fun createTestDstImage(): ImageBitmap {
+        val dst = ImageBitmap(width, height)
         val dstCanvas = Canvas(dst)
         val dstPaint = Paint().apply {
             this.color = Color.White
@@ -398,16 +398,16 @@
         val width = 200
         val height = 200
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset = ImageAsset(width, height)
+        val imageBitmap = ImageBitmap(width, height)
 
-        canvasScope.draw(Canvas(imageAsset), size) {
+        canvasScope.draw(Canvas(imageBitmap), size) {
             drawRect(color = Color.Red)
             scale(0.5f, pivot = Offset.Zero) {
                 drawRect(color = Color.Blue)
             }
         }
 
-        val pixelMap = imageAsset.toPixelMap()
+        val pixelMap = imageBitmap.toPixelMap()
         assertEquals(Color.Blue, pixelMap[0, 0])
         assertEquals(Color.Blue, pixelMap[99, 0])
         assertEquals(Color.Blue, pixelMap[0, 99])
@@ -427,16 +427,16 @@
         val width = 200
         val height = 200
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset = ImageAsset(width, height)
+        val imageBitmap = ImageBitmap(width, height)
 
-        canvasScope.draw(Canvas(imageAsset), size) {
+        canvasScope.draw(Canvas(imageBitmap), size) {
             drawRect(color = Color.Red)
             scale(0.5f) {
                 drawRect(color = Color.Blue)
             }
         }
 
-        val pixelMap = imageAsset.toPixelMap()
+        val pixelMap = imageBitmap.toPixelMap()
         val left = width / 2 - 50
         val top = height / 2 - 50
         val right = width / 2 + 50 - 1
@@ -470,10 +470,10 @@
         val width = 200
         val height = 200
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset = ImageAsset(width, height)
+        val imageBitmap = ImageBitmap(width, height)
 
         try {
-            canvasScope.draw(Canvas(imageAsset), size) {
+            canvasScope.draw(Canvas(imageBitmap), size) {
                 inset(100.0f, 0.0f, 101.0f, 0.0f) {
                     drawRect(color = Color.Red)
                 }
@@ -491,10 +491,10 @@
         val width = 200
         val height = 200
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset = ImageAsset(width, height)
+        val imageBitmap = ImageBitmap(width, height)
 
         try {
-            canvasScope.draw(Canvas(imageAsset), size) {
+            canvasScope.draw(Canvas(imageBitmap), size) {
                 inset(0.0f, 100.0f, 0.0f, 101.0f) {
                     drawRect(color = Color.Red)
                 }
@@ -517,10 +517,10 @@
         val width = 200
         val height = 200
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset = ImageAsset(width, height)
+        val imageBitmap = ImageBitmap(width, height)
 
         try {
-            canvasScope.draw(Canvas(imageAsset), size) {
+            canvasScope.draw(Canvas(imageBitmap), size) {
                 inset(0.0f, 100.0f, 0.0f, 100.0f) {
                     drawRect(color = Color.Red)
                 }
@@ -542,10 +542,10 @@
         val width = 200
         val height = 200
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset = ImageAsset(width, height)
+        val imageBitmap = ImageBitmap(width, height)
 
         try {
-            canvasScope.draw(Canvas(imageAsset), size) {
+            canvasScope.draw(Canvas(imageBitmap), size) {
                 inset(100.0f, 0.0f, 100.0f, 0.0f) {
                     drawRect(color = Color.Red)
                 }
@@ -562,16 +562,16 @@
         val width = 200
         val height = 200
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset = ImageAsset(width, height)
+        val imageBitmap = ImageBitmap(width, height)
 
-        canvasScope.draw(Canvas(imageAsset), size) {
+        canvasScope.draw(Canvas(imageBitmap), size) {
             drawRect(color = Color.Red)
             scale(0.5f, 0.5f, Offset(width.toFloat(), height.toFloat())) {
                 drawRect(color = Color.Blue)
             }
         }
 
-        val pixelMap = imageAsset.toPixelMap()
+        val pixelMap = imageBitmap.toPixelMap()
 
         val left = width - 100
         val top = height - 100
@@ -595,8 +595,8 @@
         val width = 200
         val height = 200
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset = ImageAsset(width, height)
-        CanvasDrawScope().draw(Canvas(imageAsset), size) {
+        val imageBitmap = ImageBitmap(width, height)
+        CanvasDrawScope().draw(Canvas(imageBitmap), size) {
             drawRect(color = Color.Red)
             rotate(180.0f) {
                 drawRect(
@@ -607,7 +607,7 @@
             }
         }
 
-        val pixelMap = imageAsset.toPixelMap()
+        val pixelMap = imageBitmap.toPixelMap()
         assertEquals(Color.Blue, pixelMap[0, 0])
         assertEquals(Color.Blue, pixelMap[99, 0])
         assertEquals(Color.Blue, pixelMap[0, 99])
@@ -625,8 +625,8 @@
         val width = 200
         val height = 200
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset = ImageAsset(width, height)
-        CanvasDrawScope().draw(Canvas(imageAsset), size) {
+        val imageBitmap = ImageBitmap(width, height)
+        CanvasDrawScope().draw(Canvas(imageBitmap), size) {
             drawRect(color = Color.Red)
             rotateRad(kotlin.math.PI.toFloat()) {
                 drawRect(
@@ -637,7 +637,7 @@
             }
         }
 
-        val pixelMap = imageAsset.toPixelMap()
+        val pixelMap = imageBitmap.toPixelMap()
         assertEquals(Color.Blue, pixelMap[0, 0])
         assertEquals(Color.Blue, pixelMap[99, 0])
         assertEquals(Color.Blue, pixelMap[0, 99])
@@ -655,8 +655,8 @@
         val width = 200
         val height = 200
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset = ImageAsset(width, height)
-        CanvasDrawScope().draw(Canvas(imageAsset), size) {
+        val imageBitmap = ImageBitmap(width, height)
+        CanvasDrawScope().draw(Canvas(imageBitmap), size) {
             drawRect(color = Color.Red)
             rotate(-45.0f, Offset.Zero) {
                 drawRect(
@@ -666,7 +666,7 @@
             }
         }
 
-        val pixelMap = imageAsset.toPixelMap()
+        val pixelMap = imageBitmap.toPixelMap()
         assertEquals(Color.Blue, pixelMap[2, 0])
         assertEquals(Color.Blue, pixelMap[50, 49])
         assertEquals(Color.Blue, pixelMap[70, 0])
@@ -682,8 +682,8 @@
         val width = 200
         val height = 200
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset1 = ImageAsset(width, height)
-        CanvasDrawScope().draw(Canvas(imageAsset1), size) {
+        val imageBitmap1 = ImageBitmap(width, height)
+        CanvasDrawScope().draw(Canvas(imageBitmap1), size) {
             drawRect(color = Color.Red)
             inset(20.0f, 12.0f, 10.0f, 8.0f) {
                 scale(2.0f, 0.5f) {
@@ -699,8 +699,8 @@
             }
         }
 
-        val imageAsset2 = ImageAsset(width, height)
-        val saveCountCanvas = SaveCountCanvas(Canvas(imageAsset2))
+        val imageBitmap2 = ImageBitmap(width, height)
+        val saveCountCanvas = SaveCountCanvas(Canvas(imageBitmap2))
         CanvasDrawScope().draw(saveCountCanvas, size) {
             drawRect(color = Color.Red)
             withTransform({
@@ -722,8 +722,8 @@
             assertEquals(1, saveCountCanvas.saveCount)
         }
 
-        val pixelMap1 = imageAsset1.toPixelMap()
-        val pixelMap2 = imageAsset2.toPixelMap()
+        val pixelMap1 = imageBitmap1.toPixelMap()
+        val pixelMap2 = imageBitmap2.toPixelMap()
         assertEquals(pixelMap1.width, pixelMap2.width)
         assertEquals(pixelMap1.height, pixelMap2.height)
         assertEquals(pixelMap1.stride, pixelMap2.stride)
@@ -868,7 +868,7 @@
 
     @Test
     fun testDensityAndLayoutDirectionConfigured() {
-        val canvas = Canvas(ImageAsset(1, 1))
+        val canvas = Canvas(ImageBitmap(1, 1))
         CanvasDrawScope().draw(
             Density(density = 2.0f, fontScale = 3.0f),
             LayoutDirection.Rtl,
@@ -884,8 +884,8 @@
 
     @Test
     fun testParametersRestoredAfterDraw() {
-        val canvas1 = Canvas(ImageAsset(200, 300))
-        val canvas2 = Canvas(ImageAsset(100, 200))
+        val canvas1 = Canvas(ImageBitmap(200, 300))
+        val canvas2 = Canvas(ImageBitmap(100, 200))
 
         val size1 = Size(200f, 300f)
         val size2 = Size(100f, 200f)
@@ -976,7 +976,7 @@
         val width = 100
         val height = 150
         TestDrawScopeTransform().draw(
-            Canvas(ImageAsset(width, height)),
+            Canvas(ImageBitmap(width, height)),
             Size(width.toFloat(), height.toFloat())
         ) {
             withWrappedTransform({
@@ -996,16 +996,16 @@
         canvasBlock: (Canvas) -> Unit
     ) {
         val size = Size(width.toFloat(), height.toFloat())
-        val imageAsset1 = ImageAsset(width, height)
-        CanvasDrawScope().draw(Canvas(imageAsset1), size) {
+        val imageBitmap1 = ImageBitmap(width, height)
+        CanvasDrawScope().draw(Canvas(imageBitmap1), size) {
             drawScopeBlock()
         }
 
-        val imageAsset2 = ImageAsset(width, height)
-        canvasBlock(Canvas(imageAsset2))
+        val imageBitmap2 = ImageBitmap(width, height)
+        canvasBlock(Canvas(imageBitmap2))
 
-        val pixelMap1 = imageAsset1.toPixelMap()
-        val pixelMap2 = imageAsset2.toPixelMap()
+        val pixelMap1 = imageBitmap1.toPixelMap()
+        val pixelMap2 = imageBitmap2.toPixelMap()
         assertEquals(pixelMap1.width, pixelMap2.width)
         assertEquals(pixelMap1.height, pixelMap2.height)
         assertEquals(pixelMap1.stride, pixelMap2.stride)
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/painter/ImagePainterTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/painter/ImagePainterTest.kt
index 501f178..8d4d17f 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/painter/ImagePainterTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/painter/ImagePainterTest.kt
@@ -23,7 +23,7 @@
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.graphics.toPixelMap
@@ -44,8 +44,8 @@
     val white = Color.White
     private val srcSize = Size(100.0f, 100.0f)
 
-    private fun createTestSrcImage(): ImageAsset {
-        val src = ImageAsset(100, 100)
+    private fun createTestSrcImage(): ImageBitmap {
+        val src = ImageBitmap(100, 100)
         val canvas = Canvas(src)
         val paint = Paint().apply {
             this.color = Color.Blue
@@ -57,8 +57,8 @@
         return src
     }
 
-    private fun createTestDstImage(): ImageAsset {
-        val dst = ImageAsset(200, 200)
+    private fun createTestDstImage(): ImageBitmap {
+        val dst = ImageBitmap(200, 200)
         val dstCanvas = Canvas(dst)
         val dstPaint = Paint().apply {
             this.color = Color.White
@@ -232,7 +232,7 @@
                 IntOffset(0, 0),
                 IntSize(image.width + 1, 10)
             )
-            fail("Right bound must be less than ImageAsset width")
+            fail("Right bound must be less than ImageBitmap width")
         } catch (e: IllegalArgumentException) {
             // no-op
         }
@@ -247,7 +247,7 @@
                 IntOffset(0, 0),
                 IntSize(10, image.height + 1)
             )
-            fail("Bottom bound must be less than ImageAsset height")
+            fail("Bottom bound must be less than ImageBitmap height")
         } catch (e: IllegalArgumentException) {
             // no-op
         }
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/painter/PainterTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/painter/PainterTest.kt
index 64ee193..16bc135 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/painter/PainterTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/painter/PainterTest.kt
@@ -23,7 +23,7 @@
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.graphics.drawscope.DrawScope
@@ -62,7 +62,7 @@
 
         drawPainter(
             p,
-            Canvas(ImageAsset(100, 100)),
+            Canvas(ImageBitmap(100, 100)),
             Size(100f, 100f)
         )
         assertTrue(p.didDraw)
@@ -87,7 +87,7 @@
             }
         }
 
-        val image = ImageAsset(100, 100)
+        val image = ImageBitmap(100, 100)
 
         drawPainter(
             p,
@@ -111,7 +111,7 @@
             }
         }
 
-        val image = ImageAsset(100, 100)
+        val image = ImageBitmap(100, 100)
         val canvas = Canvas(image)
 
         val paint = Paint().apply { this.color = Color.White }
@@ -164,7 +164,7 @@
         }
 
         assertEquals(Color.Red, p.color)
-        val image = ImageAsset(100, 100)
+        val image = ImageBitmap(100, 100)
         val canvas = Canvas(image)
 
         val paint = Paint().apply { this.color = Color.White }
@@ -210,7 +210,7 @@
             }
         }
 
-        val image = ImageAsset(100, 100)
+        val image = ImageBitmap(100, 100)
 
         drawPainter(
             p,
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidCanvas.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidCanvas.kt
index 08f8162..970e993 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidCanvas.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidCanvas.kt
@@ -26,9 +26,9 @@
 
 /**
  * Create a new Canvas instance that targets its drawing commands
- * to the provided [ImageAsset]
+ * to the provided [ImageBitmap]
  */
-internal actual fun ActualCanvas(image: ImageAsset): Canvas =
+internal actual fun ActualCanvas(image: ImageBitmap): Canvas =
     AndroidCanvas().apply {
         internalCanvas = android.graphics.Canvas(image.asAndroidBitmap())
     }
@@ -249,7 +249,7 @@
     /**
      * @see Canvas.drawImage
      */
-    override fun drawImage(image: ImageAsset, topLeftOffset: Offset, paint: Paint) {
+    override fun drawImage(image: ImageBitmap, topLeftOffset: Offset, paint: Paint) {
         internalCanvas.drawBitmap(
             image.asAndroidBitmap(),
             topLeftOffset.x,
@@ -262,7 +262,7 @@
      * @See Canvas.drawImageRect
      */
     override fun drawImageRect(
-        image: ImageAsset,
+        image: ImageBitmap,
         srcOffset: IntOffset,
         srcSize: IntSize,
         dstOffset: IntOffset,
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageAsset.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageAsset.kt
deleted file mode 100644
index ab6e9e1..0000000
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageAsset.kt
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.graphics
-
-import android.content.res.Resources
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import android.os.Build
-import android.util.DisplayMetrics
-import androidx.annotation.RequiresApi
-import androidx.compose.ui.graphics.colorspace.ColorSpace
-import androidx.compose.ui.graphics.colorspace.ColorSpaces
-
-/**
- * Create an [ImageAsset] from an image file stored in resources for the application
- *
- * @param res Resources object to query the image file from
- * @param resId Identifier for the image asset to query from [res]
- *
- * @return Loaded image file represented as an [ImageAsset]
- */
-fun imageFromResource(res: Resources, resId: Int): ImageAsset {
-    return AndroidImageAsset(BitmapFactory.decodeResource(res, resId))
-}
-
-/**
- * Create an [ImageAsset] from the given [Bitmap]. Note this does
- * not create a copy of the original [Bitmap] and changes to it
- * will modify the returned [ImageAsset]
- */
-fun Bitmap.asImageAsset(): ImageAsset = AndroidImageAsset(this)
-
-internal actual fun ActualImageAsset(
-    width: Int,
-    height: Int,
-    config: ImageAssetConfig,
-    hasAlpha: Boolean,
-    colorSpace: ColorSpace
-): ImageAsset {
-    val bitmapConfig = config.toBitmapConfig()
-    val bitmap: Bitmap
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-        // Note intentionally ignoring density in all cases
-        bitmap = Bitmap.createBitmap(
-            null,
-            width,
-            height,
-            bitmapConfig,
-            hasAlpha,
-            colorSpace.toFrameworkColorSpace()
-        )
-    } else {
-        bitmap = Bitmap.createBitmap(
-            null as DisplayMetrics?,
-            width,
-            height,
-            bitmapConfig
-        )
-        bitmap.setHasAlpha(hasAlpha)
-    }
-    return AndroidImageAsset(bitmap)
-}
-
-/**
- * @Throws UnsupportedOperationException if this [ImageAsset] is not backed by an
- * android.graphics.Bitmap
- */
-fun ImageAsset.asAndroidBitmap(): Bitmap =
-    when (this) {
-        is AndroidImageAsset -> bitmap
-        else -> throw UnsupportedOperationException("Unable to obtain android.graphics.Bitmap")
-    }
-
-internal class AndroidImageAsset(internal val bitmap: Bitmap) : ImageAsset {
-
-    override val width: Int
-        get() = bitmap.width
-
-    override val height: Int
-        get() = bitmap.height
-
-    override val config: ImageAssetConfig
-        get() = bitmap.config.toImageConfig()
-
-    override val colorSpace: ColorSpace
-        get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            bitmap.colorSpace?.toComposeColorSpace() ?: ColorSpaces.Srgb
-        } else {
-            ColorSpaces.Srgb
-        }
-
-    override fun readPixels(
-        buffer: IntArray,
-        startX: Int,
-        startY: Int,
-        width: Int,
-        height: Int,
-        bufferOffset: Int,
-        stride: Int
-    ) {
-        // Internal Android implementation that copies the pixels from the underlying
-        // android.graphics.Bitmap if the configuration supports it
-        val androidBitmap = asAndroidBitmap()
-        var recycleTarget = false
-        val targetBitmap =
-            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ||
-                androidBitmap.config != Bitmap.Config.HARDWARE
-            ) {
-                androidBitmap
-            } else {
-                // Because we are creating a copy for the purposes of reading pixels out of it
-                // be sure to recycle this temporary bitmap when we are finished with it.
-                recycleTarget = true
-
-                // Pixels of a hardware bitmap cannot be queried directly so make a copy
-                // of it into a configuration that can be queried
-                // Passing in false for the isMutable parameter as we only intend to read pixel
-                // information from the bitmap
-                androidBitmap.copy(Bitmap.Config.ARGB_8888, false)
-            }
-
-        targetBitmap.getPixels(
-            buffer,
-            bufferOffset,
-            stride,
-            startX,
-            startY,
-            width,
-            height
-        )
-        // Recycle the target if we are done with it
-        if (recycleTarget) {
-            targetBitmap.recycle()
-        }
-    }
-
-    override val hasAlpha: Boolean
-        get() = bitmap.hasAlpha()
-
-    override fun prepareToDraw() {
-        bitmap.prepareToDraw()
-    }
-}
-
-internal fun ImageAssetConfig.toBitmapConfig(): Bitmap.Config {
-    // Cannot utilize when statements with enums that may have different sets of supported
-    // values between the compiled SDK and the platform version of the device.
-    // As a workaround use if/else statements
-    // See https://youtrack.jetbrains.com/issue/KT-30473 for details
-    return if (this == ImageAssetConfig.Argb8888) {
-        Bitmap.Config.ARGB_8888
-    } else if (this == ImageAssetConfig.Alpha8) {
-        Bitmap.Config.ALPHA_8
-    } else if (this == ImageAssetConfig.Rgb565) {
-        Bitmap.Config.RGB_565
-    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this == ImageAssetConfig.F16) {
-        Bitmap.Config.RGBA_F16
-    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this == ImageAssetConfig.Gpu) {
-        Bitmap.Config.HARDWARE
-    } else {
-        Bitmap.Config.ARGB_8888
-    }
-}
-
-internal fun Bitmap.Config.toImageConfig(): ImageAssetConfig {
-    // Cannot utilize when statements with enums that may have different sets of supported
-    // values between the compiled SDK and the platform version of the device.
-    // As a workaround use if/else statements
-    // See https://youtrack.jetbrains.com/issue/KT-30473 for details
-    @Suppress("DEPRECATION")
-    return if (this == Bitmap.Config.ALPHA_8) {
-        ImageAssetConfig.Alpha8
-    } else if (this == Bitmap.Config.RGB_565) {
-        ImageAssetConfig.Rgb565
-    } else if (this == Bitmap.Config.ARGB_4444) {
-        ImageAssetConfig.Argb8888 // Always upgrade to Argb_8888
-    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this == Bitmap.Config.RGBA_F16) {
-        ImageAssetConfig.F16
-    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this == Bitmap.Config.HARDWARE) {
-        ImageAssetConfig.Gpu
-    } else {
-        ImageAssetConfig.Argb8888
-    }
-}
-
-@RequiresApi(Build.VERSION_CODES.O)
-internal fun ColorSpace.toFrameworkColorSpace(): android.graphics.ColorSpace {
-    val frameworkNamedSpace = when (this) {
-        ColorSpaces.Srgb -> android.graphics.ColorSpace.Named.SRGB
-        ColorSpaces.Aces -> android.graphics.ColorSpace.Named.ACES
-        ColorSpaces.Acescg -> android.graphics.ColorSpace.Named.ACESCG
-        ColorSpaces.AdobeRgb -> android.graphics.ColorSpace.Named.ADOBE_RGB
-        ColorSpaces.Bt2020 -> android.graphics.ColorSpace.Named.BT2020
-        ColorSpaces.Bt709 -> android.graphics.ColorSpace.Named.BT709
-        ColorSpaces.CieLab -> android.graphics.ColorSpace.Named.CIE_LAB
-        ColorSpaces.CieXyz -> android.graphics.ColorSpace.Named.CIE_XYZ
-        ColorSpaces.DciP3 -> android.graphics.ColorSpace.Named.DCI_P3
-        ColorSpaces.DisplayP3 -> android.graphics.ColorSpace.Named.DISPLAY_P3
-        ColorSpaces.ExtendedSrgb -> android.graphics.ColorSpace.Named.EXTENDED_SRGB
-        ColorSpaces.LinearExtendedSrgb ->
-            android.graphics.ColorSpace.Named.LINEAR_EXTENDED_SRGB
-        ColorSpaces.LinearSrgb -> android.graphics.ColorSpace.Named.LINEAR_SRGB
-        ColorSpaces.Ntsc1953 -> android.graphics.ColorSpace.Named.NTSC_1953
-        ColorSpaces.ProPhotoRgb -> android.graphics.ColorSpace.Named.PRO_PHOTO_RGB
-        ColorSpaces.SmpteC -> android.graphics.ColorSpace.Named.SMPTE_C
-        else -> android.graphics.ColorSpace.Named.SRGB
-    }
-    return android.graphics.ColorSpace.get(frameworkNamedSpace)
-}
-
-@RequiresApi(Build.VERSION_CODES.O)
-internal fun android.graphics.ColorSpace.toComposeColorSpace(): ColorSpace {
-    return when (this) {
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.SRGB)
-        -> ColorSpaces.Srgb
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.ACES)
-        -> ColorSpaces.Aces
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.ACESCG)
-        -> ColorSpaces.Acescg
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.ADOBE_RGB)
-        -> ColorSpaces.AdobeRgb
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.BT2020)
-        -> ColorSpaces.Bt2020
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.BT709)
-        -> ColorSpaces.Bt709
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.CIE_LAB)
-        -> ColorSpaces.CieLab
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.CIE_XYZ)
-        -> ColorSpaces.CieXyz
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.DCI_P3)
-        -> ColorSpaces.DciP3
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.DISPLAY_P3)
-        -> ColorSpaces.DisplayP3
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.EXTENDED_SRGB)
-        -> ColorSpaces.ExtendedSrgb
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.LINEAR_EXTENDED_SRGB)
-        -> ColorSpaces.LinearExtendedSrgb
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.LINEAR_SRGB)
-        -> ColorSpaces.LinearSrgb
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.NTSC_1953)
-        -> ColorSpaces.Ntsc1953
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.PRO_PHOTO_RGB)
-        -> ColorSpaces.ProPhotoRgb
-        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.SMPTE_C)
-        -> ColorSpaces.SmpteC
-        else -> ColorSpaces.Srgb
-    }
-}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.kt
new file mode 100644
index 0000000..eab26b4
--- /dev/null
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.kt
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.graphics
+
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.os.Build
+import android.util.DisplayMetrics
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.graphics.colorspace.ColorSpace
+import androidx.compose.ui.graphics.colorspace.ColorSpaces
+
+/**
+ * Create an [ImageBitmap] from an image file stored in resources for the application
+ *
+ * @param res Resources object to query the image file from
+ * @param resId Identifier for the image asset to query from [res]
+ *
+ * @return Loaded image file represented as an [ImageBitmap]
+ */
+fun imageFromResource(res: Resources, resId: Int): ImageBitmap {
+    return AndroidImageBitmap(BitmapFactory.decodeResource(res, resId))
+}
+
+/**
+ * Create an [ImageBitmap] from the given [Bitmap]. Note this does
+ * not create a copy of the original [Bitmap] and changes to it
+ * will modify the returned [ImageBitmap]
+ */
+fun Bitmap.asImageBitmap(): ImageBitmap = AndroidImageBitmap(this)
+
+/**
+ * Create an [ImageBitmap] from the given [Bitmap]. Note this does
+ * not create a copy of the original [Bitmap] and changes to it
+ * will modify the returned [ImageBitmap]
+ */
+@Deprecated(
+    "Use asImageBitmap instead",
+    ReplaceWith("asImageBitmap()", "androidx.compose.ui.graphics")
+)
+fun Bitmap.asImageAsset(): ImageBitmap = asImageBitmap()
+
+internal actual fun ActualImageBitmap(
+    width: Int,
+    height: Int,
+    config: ImageBitmapConfig,
+    hasAlpha: Boolean,
+    colorSpace: ColorSpace
+): ImageBitmap {
+    val bitmapConfig = config.toBitmapConfig()
+    val bitmap: Bitmap
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+        bitmap = Api26Bitmap.createBitmap(width, height, config, hasAlpha, colorSpace)
+    } else {
+        bitmap = Bitmap.createBitmap(
+            null as DisplayMetrics?,
+            width,
+            height,
+            bitmapConfig
+        )
+        bitmap.setHasAlpha(hasAlpha)
+    }
+    return AndroidImageBitmap(bitmap)
+}
+
+/**
+ * @Throws UnsupportedOperationException if this [ImageBitmap] is not backed by an
+ * android.graphics.Bitmap
+ */
+fun ImageBitmap.asAndroidBitmap(): Bitmap =
+    when (this) {
+        is AndroidImageBitmap -> bitmap
+        else -> throw UnsupportedOperationException("Unable to obtain android.graphics.Bitmap")
+    }
+
+internal class AndroidImageBitmap(internal val bitmap: Bitmap) : ImageBitmap {
+
+    override val width: Int
+        get() = bitmap.width
+
+    override val height: Int
+        get() = bitmap.height
+
+    override val config: ImageBitmapConfig
+        get() = bitmap.config.toImageConfig()
+
+    override val colorSpace: ColorSpace
+        get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            with (Api26Bitmap) {
+                bitmap.composeColorSpace()
+            }
+        } else {
+            ColorSpaces.Srgb
+        }
+
+    override fun readPixels(
+        buffer: IntArray,
+        startX: Int,
+        startY: Int,
+        width: Int,
+        height: Int,
+        bufferOffset: Int,
+        stride: Int
+    ) {
+        // Internal Android implementation that copies the pixels from the underlying
+        // android.graphics.Bitmap if the configuration supports it
+        val androidBitmap = asAndroidBitmap()
+        var recycleTarget = false
+        val targetBitmap =
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ||
+                androidBitmap.config != Bitmap.Config.HARDWARE
+            ) {
+                androidBitmap
+            } else {
+                // Because we are creating a copy for the purposes of reading pixels out of it
+                // be sure to recycle this temporary bitmap when we are finished with it.
+                recycleTarget = true
+
+                // Pixels of a hardware bitmap cannot be queried directly so make a copy
+                // of it into a configuration that can be queried
+                // Passing in false for the isMutable parameter as we only intend to read pixel
+                // information from the bitmap
+                androidBitmap.copy(Bitmap.Config.ARGB_8888, false)
+            }
+
+        targetBitmap.getPixels(
+            buffer,
+            bufferOffset,
+            stride,
+            startX,
+            startY,
+            width,
+            height
+        )
+        // Recycle the target if we are done with it
+        if (recycleTarget) {
+            targetBitmap.recycle()
+        }
+    }
+
+    override val hasAlpha: Boolean
+        get() = bitmap.hasAlpha()
+
+    override fun prepareToDraw() {
+        bitmap.prepareToDraw()
+    }
+}
+
+internal fun ImageBitmapConfig.toBitmapConfig(): Bitmap.Config {
+    // Cannot utilize when statements with enums that may have different sets of supported
+    // values between the compiled SDK and the platform version of the device.
+    // As a workaround use if/else statements
+    // See https://youtrack.jetbrains.com/issue/KT-30473 for details
+    return if (this == ImageBitmapConfig.Argb8888) {
+        Bitmap.Config.ARGB_8888
+    } else if (this == ImageBitmapConfig.Alpha8) {
+        Bitmap.Config.ALPHA_8
+    } else if (this == ImageBitmapConfig.Rgb565) {
+        Bitmap.Config.RGB_565
+    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this == ImageBitmapConfig.F16) {
+        Bitmap.Config.RGBA_F16
+    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this == ImageBitmapConfig.Gpu) {
+        Bitmap.Config.HARDWARE
+    } else {
+        Bitmap.Config.ARGB_8888
+    }
+}
+
+internal fun Bitmap.Config.toImageConfig(): ImageBitmapConfig {
+    // Cannot utilize when statements with enums that may have different sets of supported
+    // values between the compiled SDK and the platform version of the device.
+    // As a workaround use if/else statements
+    // See https://youtrack.jetbrains.com/issue/KT-30473 for details
+    @Suppress("DEPRECATION")
+    return if (this == Bitmap.Config.ALPHA_8) {
+        ImageBitmapConfig.Alpha8
+    } else if (this == Bitmap.Config.RGB_565) {
+        ImageBitmapConfig.Rgb565
+    } else if (this == Bitmap.Config.ARGB_4444) {
+        ImageBitmapConfig.Argb8888 // Always upgrade to Argb_8888
+    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this == Bitmap.Config.RGBA_F16) {
+        ImageBitmapConfig.F16
+    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this == Bitmap.Config.HARDWARE) {
+        ImageBitmapConfig.Gpu
+    } else {
+        ImageBitmapConfig.Argb8888
+    }
+}
+
+/**
+ * Make Lint happy
+ * Separate class to contain all API calls that require API level 26 to assist in dead code
+ * elimination during compilation time
+ */
+private class Api26Bitmap {
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    companion object {
+
+        internal fun createBitmap(
+            width: Int,
+            height: Int,
+            bitmapConfig: ImageBitmapConfig,
+            hasAlpha: Boolean,
+            colorSpace: ColorSpace
+        ): Bitmap {
+            // Note intentionally ignoring density in all cases
+            return Bitmap.createBitmap(
+                null,
+                width,
+                height,
+                bitmapConfig.toBitmapConfig(),
+                hasAlpha,
+                colorSpace.toFrameworkColorSpace()
+            )
+        }
+
+        internal fun Bitmap.composeColorSpace() =
+            colorSpace?.composeColorSpace() ?: ColorSpaces.Srgb
+
+        internal fun ColorSpace.toFrameworkColorSpace(): android.graphics.ColorSpace {
+            val frameworkNamedSpace = when (this) {
+                ColorSpaces.Srgb -> android.graphics.ColorSpace.Named.SRGB
+                ColorSpaces.Aces -> android.graphics.ColorSpace.Named.ACES
+                ColorSpaces.Acescg -> android.graphics.ColorSpace.Named.ACESCG
+                ColorSpaces.AdobeRgb -> android.graphics.ColorSpace.Named.ADOBE_RGB
+                ColorSpaces.Bt2020 -> android.graphics.ColorSpace.Named.BT2020
+                ColorSpaces.Bt709 -> android.graphics.ColorSpace.Named.BT709
+                ColorSpaces.CieLab -> android.graphics.ColorSpace.Named.CIE_LAB
+                ColorSpaces.CieXyz -> android.graphics.ColorSpace.Named.CIE_XYZ
+                ColorSpaces.DciP3 -> android.graphics.ColorSpace.Named.DCI_P3
+                ColorSpaces.DisplayP3 -> android.graphics.ColorSpace.Named.DISPLAY_P3
+                ColorSpaces.ExtendedSrgb -> android.graphics.ColorSpace.Named.EXTENDED_SRGB
+                ColorSpaces.LinearExtendedSrgb ->
+                    android.graphics.ColorSpace.Named.LINEAR_EXTENDED_SRGB
+                ColorSpaces.LinearSrgb -> android.graphics.ColorSpace.Named.LINEAR_SRGB
+                ColorSpaces.Ntsc1953 -> android.graphics.ColorSpace.Named.NTSC_1953
+                ColorSpaces.ProPhotoRgb -> android.graphics.ColorSpace.Named.PRO_PHOTO_RGB
+                ColorSpaces.SmpteC -> android.graphics.ColorSpace.Named.SMPTE_C
+                else -> android.graphics.ColorSpace.Named.SRGB
+            }
+            return android.graphics.ColorSpace.get(frameworkNamedSpace)
+        }
+
+        internal fun android.graphics.ColorSpace.composeColorSpace(): ColorSpace {
+            return when (this) {
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.SRGB) ->
+                    ColorSpaces.Srgb
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.ACES) ->
+                    ColorSpaces.Aces
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.ACESCG) ->
+                    ColorSpaces.Acescg
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.ADOBE_RGB) ->
+                    ColorSpaces.AdobeRgb
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.BT2020) ->
+                    ColorSpaces.Bt2020
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.BT709) ->
+                    ColorSpaces.Bt709
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.CIE_LAB) ->
+                    ColorSpaces.CieLab
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.CIE_XYZ) ->
+                    ColorSpaces.CieXyz
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.DCI_P3) ->
+                    ColorSpaces.DciP3
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.DISPLAY_P3) ->
+                    ColorSpaces.DisplayP3
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.EXTENDED_SRGB) ->
+                    ColorSpaces.ExtendedSrgb
+                android.graphics.ColorSpace.get(
+                    android.graphics.ColorSpace.Named.LINEAR_EXTENDED_SRGB
+                ) ->
+                    ColorSpaces.LinearExtendedSrgb
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.LINEAR_SRGB) ->
+                    ColorSpaces.LinearSrgb
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.NTSC_1953) ->
+                    ColorSpaces.Ntsc1953
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.PRO_PHOTO_RGB) ->
+                    ColorSpaces.ProPhotoRgb
+                android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.SMPTE_C) ->
+                    ColorSpaces.SmpteC
+                else -> ColorSpaces.Srgb
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidShader.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidShader.kt
index 7704d47..f0918e2b 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidShader.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidShader.kt
@@ -61,7 +61,7 @@
 }
 
 internal actual fun ActualImageShader(
-    image: ImageAsset,
+    image: ImageBitmap,
     tileModeX: TileMode,
     tileModeY: TileMode
 ): Shader {
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Canvas.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Canvas.kt
index 5f181b5..1cd50b9 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Canvas.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Canvas.kt
@@ -23,11 +23,11 @@
 
 /**
  * Create a new Canvas instance that targets its drawing commands
- * to the provided [ImageAsset]
+ * to the provided [ImageBitmap]
  */
-fun Canvas(image: ImageAsset): Canvas = ActualCanvas(image)
+fun Canvas(image: ImageBitmap): Canvas = ActualCanvas(image)
 
-internal expect fun ActualCanvas(image: ImageAsset): Canvas
+internal expect fun ActualCanvas(image: ImageBitmap): Canvas
 
 expect class NativeCanvas
 
@@ -535,10 +535,10 @@
     fun drawPath(path: Path, paint: Paint)
 
     /**
-     * Draws the given [ImageAsset] into the canvas with its top-left corner at the
+     * Draws the given [ImageBitmap] into the canvas with its top-left corner at the
      * given [Offset]. The image is composited into the canvas using the given [Paint].
      */
-    fun drawImage(image: ImageAsset, topLeftOffset: Offset, paint: Paint)
+    fun drawImage(image: ImageBitmap, topLeftOffset: Offset, paint: Paint)
 
     /**
      * Draws the subset of the given image described by the `src` argument into
@@ -547,7 +547,7 @@
      * This might sample from outside the `src` rect by up to half the width of
      * an applied filter.
      *
-     * @param image ImageAsset to draw
+     * @param image ImageBitmap to draw
      * @param srcOffset: Optional offset representing the top left offset of the source image
      * to draw, this defaults to the origin of [image]
      * @param srcSize: Optional dimensions of the source image to draw relative to [srcOffset],
@@ -555,10 +555,10 @@
      * @param dstOffset: Offset representing the top left offset of the destination image
      * to draw
      * @param dstSize: Dimensions of the destination to draw
-     * @param paint Paint used to composite the [ImageAsset] pixels into the canvas
+     * @param paint Paint used to composite the [ImageBitmap] pixels into the canvas
      */
     fun drawImageRect(
-        image: ImageAsset,
+        image: ImageBitmap,
         srcOffset: IntOffset = IntOffset.Zero,
         srcSize: IntSize = IntSize(image.width, image.height),
         dstOffset: IntOffset = IntOffset.Zero,
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/ImageAsset.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.kt
similarity index 74%
rename from compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/ImageAsset.kt
rename to compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.kt
index 8b051d3..2029713 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/ImageAsset.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.kt
@@ -23,49 +23,49 @@
  * Graphics object that represents a 2 dimensional array of pixel information represented
  * as ARGB values
  */
-interface ImageAsset {
+interface ImageBitmap {
 
-    /** The number of image pixels along the ImageAssets's horizontal axis. */
+    /** The number of image pixels along the ImageBitmap's horizontal axis. */
     val width: Int
 
-    /** The number of image pixels along the ImageAssets's vertical axis. */
+    /** The number of image pixels along the ImageBitmap's vertical axis. */
     val height: Int
 
     /** ColorSpace the Image renders in **/
     val colorSpace: ColorSpace
 
-    /** Determines whether or not the ImageAsset contains an alpha channel **/
+    /** Determines whether or not the ImageBitmap contains an alpha channel **/
     val hasAlpha: Boolean
 
     /**
      * Returns the current configuration of this Image, either:
-     * @see ImageAssetConfig.Argb8888
-     * @see ImageAssetConfig.Rgb565
-     * @see ImageAssetConfig.Alpha8
-     * @see ImageAssetConfig.Gpu
+     * @see ImageBitmapConfig.Argb8888
+     * @see ImageBitmapConfig.Rgb565
+     * @see ImageBitmapConfig.Alpha8
+     * @see ImageBitmapConfig.Gpu
      */
-    val config: ImageAssetConfig
+    val config: ImageBitmapConfig
 
     /**
-     * Copies the pixel data within the ImageAsset into the given array. Each value is
+     * Copies the pixel data within the ImageBitmap into the given array. Each value is
      * represented as ARGB values packed into an Int.
      * The stride parameter allows the caller to allow for gaps in the returned pixels array
      * between rows. For normal packed, results, the stride value is equivalent to the width of
-     * the [ImageAsset]. The returned colors are non-premultiplied ARGB values in the
+     * the [ImageBitmap]. The returned colors are non-premultiplied ARGB values in the
      * [ColorSpaces.Srgb] color space.
      *
      * Note this method can block so it is recommended to not invoke this method in performance
      * critical code paths
      *
-     * @sample androidx.compose.ui.graphics.samples.ImageAssetReadPixelsSample
+     * @sample androidx.compose.ui.graphics.samples.ImageBitmapReadPixelsSample
      *
-     * @param buffer The array to store the [ImageAsset]'s colors. By default this allocates an
+     * @param buffer The array to store the [ImageBitmap]'s colors. By default this allocates an
      * [IntArray] large enough to store all the pixel information. Consumers of this API are
      * advised to use the smallest [IntArray] necessary to extract relevant pixel information, that
-     * is the 2 dimensional area of the section of the [ImageAsset] to be queried.
+     * is the 2 dimensional area of the section of the [ImageBitmap] to be queried.
      *
-     * @param startX The x-coordinate of the first pixel to read from the [ImageAsset]
-     * @param startY The y-coordinate of the first pixel to read from the [ImageAsset]
+     * @param startX The x-coordinate of the first pixel to read from the [ImageBitmap]
+     * @param startY The y-coordinate of the first pixel to read from the [ImageBitmap]
      * @param width The number of pixels to read from each row
      * @param height The number of rows to read
      * @param bufferOffset The first index to write into the buffer array, this defaults to 0
@@ -82,34 +82,34 @@
     )
 
     /**
-     * Builds caches associated with the ImageAsset that are used for drawing it. This method can
+     * Builds caches associated with the ImageBitmap that are used for drawing it. This method can
      * be used as a signal to upload textures to the GPU to eventually be rendered
      */
     fun prepareToDraw()
 }
 
 /**
- * Convenience method to extract pixel information from the given ImageAsset into a [PixelMap]
+ * Convenience method to extract pixel information from the given ImageBitmap into a [PixelMap]
  * that supports for querying pixel information based on
  *
  * Note this method can block so it is recommended to not invoke this method in performance
  * critical code paths
  *
- * @sample androidx.compose.ui.graphics.samples.ImageAssetToPixelMapSample
+ * @sample androidx.compose.ui.graphics.samples.ImageBitmapToPixelMapSample
  *
- * @param startX The x-coordinate of the first pixel to read from the [ImageAsset]
- * @param startY The y-coordinate of the first pixel to read from the [ImageAsset]
+ * @param startX The x-coordinate of the first pixel to read from the [ImageBitmap]
+ * @param startY The y-coordinate of the first pixel to read from the [ImageBitmap]
  * @param width The number of pixels to read from each row
  * @param height The number of rows to read
- * @param buffer The array to store the [ImageAsset]'s colors. By default this allocates an
+ * @param buffer The array to store the [ImageBitmap]'s colors. By default this allocates an
  * [IntArray] large enough to store all the pixel information. Consumers of this API are
  * advised to use the smallest [IntArray] necessary to extract relevant pixel information
  * @param bufferOffset The first index to write into the buffer array, this defaults to 0
  * @param stride The number of entries in [buffer] to skip between rows (must be >= [width]
  *
- * @see ImageAsset.readPixels
+ * @see ImageBitmap.readPixels
  */
-fun ImageAsset.toPixelMap(
+fun ImageBitmap.toPixelMap(
     startX: Int = 0,
     startY: Int = 0,
     width: Int = this.width,
@@ -131,11 +131,11 @@
 }
 
 /**
- * Possible ImageAsset configurations. An ImageAsset configuration describes
+ * Possible ImageBitmap configurations. An ImageBitmap configuration describes
  * how pixels are stored. This affects the quality (color depth) as
  * well as the ability to display transparent/translucent colors.
  */
-enum class ImageAssetConfig {
+enum class ImageBitmapConfig {
     /**
      * Each pixel is stored on 4 bytes. Each channel (RGB and alpha
      * for translucency) is stored with 8 bits of precision (256
@@ -208,30 +208,51 @@
     F16,
 
     /**
-     * Special configuration, when an ImageAsset is stored only in graphic memory.
-     * ImageAssets in this configuration are always immutable.
+     * Special configuration, when an ImageBitmap is stored only in graphic memory.
+     * ImageBitmaps in this configuration are always immutable.
      *
-     * It is optimal for cases, when the only operation with the ImageAsset is to draw it on a
+     * It is optimal for cases, when the only operation with the ImageBitmap is to draw it on a
      * screen.
      */
     Gpu
 }
 
-internal expect fun ActualImageAsset(
+internal expect fun ActualImageBitmap(
     width: Int,
     height: Int,
-    config: ImageAssetConfig,
+    config: ImageBitmapConfig,
     hasAlpha: Boolean,
     colorSpace: ColorSpace
-): ImageAsset
+): ImageBitmap
 
+fun ImageBitmap(
+    width: Int,
+    height: Int,
+    config: ImageBitmapConfig = ImageBitmapConfig.Argb8888,
+    hasAlpha: Boolean = true,
+    colorSpace: ColorSpace = ColorSpaces.Srgb
+): ImageBitmap = ActualImageBitmap(
+    width,
+    height,
+    config,
+    hasAlpha,
+    colorSpace
+)
+
+@Deprecated(
+    "Use ImageBitmap instead",
+    ReplaceWith(
+        "ImageBitmap(width, height, config, hasAlpha, colorSpace)",
+        "androidx.compose.ui.graphics"
+    )
+)
 fun ImageAsset(
     width: Int,
     height: Int,
-    config: ImageAssetConfig = ImageAssetConfig.Argb8888,
+    config: ImageBitmapConfig = ImageBitmapConfig.Argb8888,
     hasAlpha: Boolean = true,
     colorSpace: ColorSpace = ColorSpaces.Srgb
-): ImageAsset = ActualImageAsset(
+): ImageBitmap = ActualImageBitmap(
     width,
     height,
     config,
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/PixelMap.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/PixelMap.kt
index e6f3cb1..b155107 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/PixelMap.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/PixelMap.kt
@@ -19,21 +19,21 @@
 import androidx.compose.ui.util.annotation.IntRange
 
 /**
- * Result of a pixel read operation. This contains the [ImageAsset] pixel information represented
+ * Result of a pixel read operation. This contains the [ImageBitmap] pixel information represented
  * as a 1 dimensional array of values that supports queries of pixel values based on the 2
- * dimensional coordinates of the corresponding [ImageAsset] this was obtained from
+ * dimensional coordinates of the corresponding [ImageBitmap] this was obtained from
  *
- * @sample androidx.compose.ui.graphics.samples.ImageAssetReadPixelsSample
+ * @sample androidx.compose.ui.graphics.samples.ImageBitmapReadPixelsSample
  *
  * @param buffer IntArray where pixel information is stored as an ARGB value packed into an Int
- * @param bufferOffset first index in the buffer where pixel information for the [ImageAsset] is
+ * @param bufferOffset first index in the buffer where pixel information for the [ImageBitmap] is
  * stored
- * @param width Width of the subsection of the [ImageAsset] this buffer represents
- * @param height Height of the subsection of the [ImageAsset] this buffer represents
+ * @param width Width of the subsection of the [ImageBitmap] this buffer represents
+ * @param height Height of the subsection of the [ImageBitmap] this buffer represents
  * @param stride Number of entries to skip between rows
  *
- * @see ImageAsset.readPixels
- * @See ImageAsset.toPixelMap
+ * @see ImageBitmap.readPixels
+ * @See ImageBitmap.toPixelMap
  */
 class PixelMap(
     val buffer: IntArray,
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt
index c24b2ae..ed91332 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt
@@ -89,13 +89,13 @@
 ): Shader
 
 fun ImageShader(
-    image: ImageAsset,
+    image: ImageBitmap,
     tileModeX: TileMode = TileMode.Clamp,
     tileModeY: TileMode = TileMode.Clamp
 ): Shader = ActualImageShader(image, tileModeX, tileModeY)
 
 internal expect fun ActualImageShader(
-    image: ImageAsset,
+    image: ImageBitmap,
     tileModeX: TileMode,
     tileModeY: TileMode
 ): Shader
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt
index 8b0f29c..a73b4a4 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt
@@ -25,7 +25,7 @@
 import androidx.compose.ui.graphics.ClipOp
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Matrix
 import androidx.compose.ui.graphics.NativePathEffect
 import androidx.compose.ui.graphics.Paint
@@ -193,7 +193,7 @@
      * @see [DrawScope.drawImage]
      */
     override fun drawImage(
-        image: ImageAsset,
+        image: ImageBitmap,
         topLeft: Offset,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float,
         style: DrawStyle,
@@ -209,7 +209,7 @@
      * @see [DrawScope.drawImage]
      */
     override fun drawImage(
-        image: ImageAsset,
+        image: ImageBitmap,
         srcOffset: IntOffset,
         srcSize: IntSize,
         dstOffset: IntOffset,
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt
index f04d876..16f8b4f 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt
@@ -25,7 +25,7 @@
 import androidx.compose.ui.graphics.ClipOp
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.NativePathEffect
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.Path
@@ -409,10 +409,10 @@
     )
 
     /**
-     * Draws the given [ImageAsset] into the canvas with its top-left corner at the
+     * Draws the given [ImageBitmap] into the canvas with its top-left corner at the
      * given [Offset]. The image is composited into the canvas using the given [Paint].
      *
-     * @param image The [ImageAsset] to draw
+     * @param image The [ImageBitmap] to draw
      * @param topLeft Offset from the local origin of 0, 0 relative to the current translation
      * @param alpha Opacity to be applied to [image] from 0.0f to 1.0f representing
      * fully transparent to fully opaque respectively
@@ -421,7 +421,7 @@
      * @param blendMode Blending algorithm to apply to destination
      */
     fun drawImage(
-        image: ImageAsset,
+        image: ImageBitmap,
         topLeft: Offset = Offset.Zero,
         @FloatRange(from = 0.0, to = 1.0) alpha: Float = 1.0f,
         style: DrawStyle = Fill,
@@ -452,7 +452,7 @@
      * @param blendMode Blending algorithm to apply to destination
      */
     fun drawImage(
-        image: ImageAsset,
+        image: ImageBitmap,
         srcOffset: IntOffset = IntOffset.Zero,
         srcSize: IntSize = IntSize(image.width, image.height),
         dstOffset: IntOffset = IntOffset.Zero,
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/EmptyCanvas.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/EmptyCanvas.kt
index fa7014a..e09df38 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/EmptyCanvas.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/EmptyCanvas.kt
@@ -21,7 +21,7 @@
 import androidx.compose.ui.graphics.BlendMode
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.ClipOp
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.PointMode
@@ -126,12 +126,12 @@
         throw UnsupportedOperationException()
     }
 
-    override fun drawImage(image: ImageAsset, topLeftOffset: Offset, paint: Paint) {
+    override fun drawImage(image: ImageBitmap, topLeftOffset: Offset, paint: Paint) {
         throw UnsupportedOperationException()
     }
 
     override fun drawImageRect(
-        image: ImageAsset,
+        image: ImageBitmap,
         srcOffset: IntOffset,
         srcSize: IntSize,
         dstOffset: IntOffset,
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/painter/ImagePainter.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/painter/ImagePainter.kt
index 9211d42..d0223d1 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/painter/ImagePainter.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/painter/ImagePainter.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
@@ -26,12 +26,12 @@
 import kotlin.math.roundToInt
 
 /**
- * [Painter] implementation used to draw an [ImageAsset] into the provided canvas
+ * [Painter] implementation used to draw an [ImageBitmap] into the provided canvas
  * This implementation can handle applying alpha and [ColorFilter] to it's drawn result
  *
- * @param image The [ImageAsset] to draw
+ * @param image The [ImageBitmap] to draw
  * @param srcOffset Optional offset relative to [image] used to draw a subsection of the
- * [ImageAsset]. By default this uses the origin of [image]
+ * [ImageBitmap]. By default this uses the origin of [image]
  * @param srcSize Optional dimensions representing size of the subsection of [image] to draw
  * Both the offset and size must have the following requirements:
  *
@@ -40,7 +40,7 @@
  * 3) Source size must be less than or equal to the dimensions of [image]
  */
 data class ImagePainter(
-    private val image: ImageAsset,
+    private val image: ImageBitmap,
     private val srcOffset: IntOffset = IntOffset.Zero,
     private val srcSize: IntSize = IntSize(image.width, image.height)
 ) : Painter() {
@@ -66,7 +66,7 @@
     }
 
     /**
-     * Return the dimension of the underlying [ImageAsset] as it's intrinsic width and height
+     * Return the dimension of the underlying [ImageBitmap] as it's intrinsic width and height
      */
     override val intrinsicSize: Size get() = size.toSize()
 
diff --git a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopCanvas.kt b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopCanvas.kt
index 2432a0d..43b19d1 100644
--- a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopCanvas.kt
+++ b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopCanvas.kt
@@ -30,10 +30,10 @@
 
 actual typealias NativeCanvas = org.jetbrains.skija.Canvas
 
-internal actual fun ActualCanvas(image: ImageAsset): Canvas {
+internal actual fun ActualCanvas(image: ImageBitmap): Canvas {
     val skijaBitmap = image.asDesktopBitmap()
     require(!skijaBitmap.isImmutable) {
-        "Cannot draw on immutable ImageAsset"
+        "Cannot draw on immutable ImageBitmap"
     }
     return DesktopCanvas(org.jetbrains.skija.Canvas(skijaBitmap))
 }
@@ -162,7 +162,7 @@
         skija.drawPath(path.asDesktopPath(), paint.skija)
     }
 
-    override fun drawImage(image: ImageAsset, topLeftOffset: Offset, paint: Paint) {
+    override fun drawImage(image: ImageBitmap, topLeftOffset: Offset, paint: Paint) {
         skija.drawBitmapRect(
             image.asDesktopBitmap(),
             SkijaRect.makeXYWH(
@@ -182,7 +182,7 @@
     }
 
     override fun drawImageRect(
-        image: ImageAsset,
+        image: ImageBitmap,
         srcOffset: IntOffset,
         srcSize: IntSize,
         dstOffset: IntOffset,
diff --git a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopImageAsset.kt b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopImageAsset.kt
index 30cbcba..f67add9 100644
--- a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopImageAsset.kt
+++ b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopImageAsset.kt
@@ -29,16 +29,16 @@
 import kotlin.math.abs
 
 /**
- * Create an [ImageAsset] from the given [Bitmap]. Note this does
+ * Create an [ImageBitmap] from the given [Bitmap]. Note this does
  * not create a copy of the original [Bitmap] and changes to it
- * will modify the returned [ImageAsset]
+ * will modify the returned [ImageBitmap]
  */
-fun Bitmap.asImageAsset(): ImageAsset = DesktopImageAsset(this)
+fun Bitmap.asImageBitmap(): ImageBitmap = DesktopImageBitmap(this)
 
 /**
- * Create an [ImageAsset] from the given [Image].
+ * Create an [ImageBitmap] from the given [Image].
  */
-fun Image.asImageAsset(): ImageAsset = DesktopImageAsset(toBitmap())
+fun Image.asImageBitmap(): ImageBitmap = DesktopImageBitmap(toBitmap())
 
 private fun Image.toBitmap(): Bitmap {
     val bitmap = Bitmap()
@@ -49,13 +49,13 @@
     return bitmap
 }
 
-internal actual fun ActualImageAsset(
+internal actual fun ActualImageBitmap(
     width: Int,
     height: Int,
-    config: ImageAssetConfig,
+    config: ImageBitmapConfig,
     hasAlpha: Boolean,
     colorSpace: ColorSpace
-): ImageAsset {
+): ImageBitmap {
     val colorType = config.toSkijaColorType()
     val alphaType = if (hasAlpha) ColorAlphaType.PREMUL else ColorAlphaType.OPAQUE
     val skijaColorSpace = colorSpace.toSkijaColorSpace()
@@ -63,18 +63,18 @@
     val imageInfo = ImageInfo(colorInfo, width, height)
     val bitmap = Bitmap()
     bitmap.allocPixels(imageInfo)
-    return DesktopImageAsset(bitmap)
+    return DesktopImageBitmap(bitmap)
 }
 
 /**
- * Create an [ImageAsset] from an image file stored in resources for the application
+ * Create an [ImageBitmap] from an image file stored in resources for the application
  *
  * @param path path to the image file
  *
- * @return Loaded image file represented as an [ImageAsset]
+ * @return Loaded image file represented as an [ImageBitmap]
  */
-fun imageFromResource(path: String): ImageAsset =
-    Image.makeFromEncoded(loadResource(path)).asImageAsset()
+fun imageFromResource(path: String): ImageBitmap =
+    Image.makeFromEncoded(loadResource(path)).asImageBitmap()
 
 private fun loadResource(path: String): ByteArray {
     val resource = Thread.currentThread().contextClassLoader.getResource(path)
@@ -83,16 +83,16 @@
 }
 
 /**
- * @Throws UnsupportedOperationException if this [ImageAsset] is not backed by an
+ * @Throws UnsupportedOperationException if this [ImageBitmap] is not backed by an
  * org.jetbrains.skija.Image
  */
-fun ImageAsset.asDesktopBitmap(): Bitmap =
+fun ImageBitmap.asDesktopBitmap(): Bitmap =
     when (this) {
-        is DesktopImageAsset -> bitmap
+        is DesktopImageBitmap -> bitmap
         else -> throw UnsupportedOperationException("Unable to obtain org.jetbrains.skija.Image")
     }
 
-private class DesktopImageAsset(val bitmap: Bitmap) : ImageAsset {
+private class DesktopImageBitmap(val bitmap: Bitmap) : ImageBitmap {
     override val colorSpace = bitmap.colorSpace.toComposeColorSpace()
     override val config = bitmap.colorType.toComposeConfig()
     override val hasAlpha = !bitmap.isOpaque
@@ -140,20 +140,20 @@
 //  in toSkijaColorType/toComposeConfig/toComposeColorSpace/toSkijaColorSpace
 //  see [https://android-review.googlesource.com/c/platform/frameworks/support/+/1429835/comment/c219501b_63c3d1fe/]
 
-private fun ImageAssetConfig.toSkijaColorType() = when (this) {
-    ImageAssetConfig.Argb8888 -> ColorType.N32
-    ImageAssetConfig.Alpha8 -> ColorType.ALPHA_8
-    ImageAssetConfig.Rgb565 -> ColorType.RGB_565
-    ImageAssetConfig.F16 -> ColorType.RGBA_F16
+private fun ImageBitmapConfig.toSkijaColorType() = when (this) {
+    ImageBitmapConfig.Argb8888 -> ColorType.N32
+    ImageBitmapConfig.Alpha8 -> ColorType.ALPHA_8
+    ImageBitmapConfig.Rgb565 -> ColorType.RGB_565
+    ImageBitmapConfig.F16 -> ColorType.RGBA_F16
     else -> ColorType.N32
 }
 
 private fun ColorType.toComposeConfig() = when (this) {
-    ColorType.N32 -> ImageAssetConfig.Argb8888
-    ColorType.ALPHA_8 -> ImageAssetConfig.Alpha8
-    ColorType.RGB_565 -> ImageAssetConfig.Rgb565
-    ColorType.RGBA_F16 -> ImageAssetConfig.F16
-    else -> ImageAssetConfig.Argb8888
+    ColorType.N32 -> ImageBitmapConfig.Argb8888
+    ColorType.ALPHA_8 -> ImageBitmapConfig.Alpha8
+    ColorType.RGB_565 -> ImageBitmapConfig.Rgb565
+    ColorType.RGBA_F16 -> ImageBitmapConfig.F16
+    else -> ImageBitmapConfig.Argb8888
 }
 
 private fun org.jetbrains.skija.ColorSpace?.toComposeColorSpace(): ColorSpace {
diff --git a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopShader.kt b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopShader.kt
index ccd6fb3..0287398 100644
--- a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopShader.kt
+++ b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopShader.kt
@@ -55,7 +55,7 @@
 }
 
 internal actual fun ActualImageShader(
-    image: ImageAsset,
+    image: ImageBitmap,
     tileModeX: TileMode,
     tileModeY: TileMode
 ): Shader {
diff --git a/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopImageAssetTest.kt b/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopImageBitmapTest.kt
similarity index 87%
rename from compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopImageAssetTest.kt
rename to compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopImageBitmapTest.kt
index 830c669..30da869 100644
--- a/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopImageAssetTest.kt
+++ b/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopImageBitmapTest.kt
@@ -23,12 +23,12 @@
 import org.junit.Assert.assertTrue
 import org.junit.Test
 
-class DesktopImageAssetTest : DesktopGraphicsTest() {
+class DesktopImageBitmapTest : DesktopGraphicsTest() {
     private val canvas: Canvas = initCanvas(widthPx = 16, heightPx = 16)
 
     @Test
-    fun drawOnImageAssetAlpha8() {
-        val asset = ImageAsset(width = 8, height = 8, config = ImageAssetConfig.Alpha8)
+    fun drawOnImageBitmapAlpha8() {
+        val asset = ImageBitmap(width = 8, height = 8, config = ImageBitmapConfig.Alpha8)
         val assetCanvas = Canvas(asset)
         assetCanvas.drawImage(
             imageFromResource("androidx/compose/desktop/test.png"),
@@ -41,8 +41,8 @@
     }
 
     @Test
-    fun drawOnImageAssetDisplayP3() {
-        val asset = ImageAsset(width = 8, height = 8, colorSpace = ColorSpaces.DisplayP3)
+    fun drawOnImageBitmapDisplayP3() {
+        val asset = ImageBitmap(width = 8, height = 8, colorSpace = ColorSpaces.DisplayP3)
         val assetCanvas = Canvas(asset)
         assetCanvas.drawImage(
             imageFromResource("androidx/compose/desktop/test.png"),
@@ -55,8 +55,8 @@
     }
 
     @Test
-    fun drawOnImageAsset() {
-        val asset = ImageAsset(width = 8, height = 8)
+    fun drawOnImageBitmap() {
+        val asset = ImageBitmap(width = 8, height = 8)
         val assetCanvas = Canvas(asset)
         assetCanvas.drawImage(
             imageFromResource("androidx/compose/desktop/test.png"),
@@ -69,7 +69,7 @@
     }
 
     @Test(expected = RuntimeException::class)
-    fun `cannot draw on loaded ImageAsset`() {
+    fun `cannot draw on loaded ImageBitmap`() {
         val asset = imageFromResource("androidx/compose/desktop/test.png")
         Canvas(asset)
     }
@@ -80,7 +80,7 @@
         assertEquals(8, asset.width)
         assertEquals(8, asset.height)
         assertTrue(asset.hasAlpha)
-        assertEquals(ImageAssetConfig.Argb8888, asset.config)
+        assertEquals(ImageBitmapConfig.Argb8888, asset.config)
         assertEquals(ColorSpaces.Srgb, asset.colorSpace)
     }
 
diff --git a/compose/ui/ui-test/api/current.txt b/compose/ui/ui-test/api/current.txt
index 2216d25..c4d58cd 100644
--- a/compose/ui/ui-test/api/current.txt
+++ b/compose/ui/ui-test/api/current.txt
@@ -20,8 +20,8 @@
     method @Deprecated public static void assertShape-WOPiG5A(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, float horizontalPadding, float verticalPadding, long backgroundColor, long shapeColor, optional androidx.compose.ui.graphics.Shape shape, optional float shapeOverlapPixelCount);
     method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(androidx.compose.ui.test.SemanticsNodeInteraction);
     method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(android.view.View);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(android.view.View);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageBitmap captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageBitmap captureToImage(android.view.View);
     method @Deprecated public static boolean contains-ej0GBII(androidx.compose.ui.graphics.Path, long offset);
   }
 
@@ -33,6 +33,7 @@
 
   public final class AnimationClocksKt {
     method @androidx.compose.ui.test.ExperimentalTesting public static androidx.compose.animation.core.MonotonicFrameAnimationClock monotonicFrameAnimationClockOf(kotlin.coroutines.CoroutineContext coroutineContext, androidx.compose.runtime.dispatch.MonotonicFrameClock clock);
+    method @androidx.compose.ui.test.ExperimentalTesting public static androidx.compose.animation.core.MonotonicFrameAnimationClock monotonicFrameAnimationClockOf(kotlin.coroutines.CoroutineContext coroutineContext);
   }
 
   public final class AssertionsKt {
diff --git a/compose/ui/ui-test/api/public_plus_experimental_current.txt b/compose/ui/ui-test/api/public_plus_experimental_current.txt
index 2216d25..c4d58cd 100644
--- a/compose/ui/ui-test/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-test/api/public_plus_experimental_current.txt
@@ -20,8 +20,8 @@
     method @Deprecated public static void assertShape-WOPiG5A(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, float horizontalPadding, float verticalPadding, long backgroundColor, long shapeColor, optional androidx.compose.ui.graphics.Shape shape, optional float shapeOverlapPixelCount);
     method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(androidx.compose.ui.test.SemanticsNodeInteraction);
     method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(android.view.View);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(android.view.View);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageBitmap captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageBitmap captureToImage(android.view.View);
     method @Deprecated public static boolean contains-ej0GBII(androidx.compose.ui.graphics.Path, long offset);
   }
 
@@ -33,6 +33,7 @@
 
   public final class AnimationClocksKt {
     method @androidx.compose.ui.test.ExperimentalTesting public static androidx.compose.animation.core.MonotonicFrameAnimationClock monotonicFrameAnimationClockOf(kotlin.coroutines.CoroutineContext coroutineContext, androidx.compose.runtime.dispatch.MonotonicFrameClock clock);
+    method @androidx.compose.ui.test.ExperimentalTesting public static androidx.compose.animation.core.MonotonicFrameAnimationClock monotonicFrameAnimationClockOf(kotlin.coroutines.CoroutineContext coroutineContext);
   }
 
   public final class AssertionsKt {
diff --git a/compose/ui/ui-test/api/restricted_current.txt b/compose/ui/ui-test/api/restricted_current.txt
index 2216d25..c4d58cd 100644
--- a/compose/ui/ui-test/api/restricted_current.txt
+++ b/compose/ui/ui-test/api/restricted_current.txt
@@ -20,8 +20,8 @@
     method @Deprecated public static void assertShape-WOPiG5A(android.graphics.Bitmap, androidx.compose.ui.unit.Density density, float horizontalPadding, float verticalPadding, long backgroundColor, long shapeColor, optional androidx.compose.ui.graphics.Shape shape, optional float shapeOverlapPixelCount);
     method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(androidx.compose.ui.test.SemanticsNodeInteraction);
     method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.Bitmap captureToBitmap(android.view.View);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
-    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageAsset captureToImage(android.view.View);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageBitmap captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageBitmap captureToImage(android.view.View);
     method @Deprecated public static boolean contains-ej0GBII(androidx.compose.ui.graphics.Path, long offset);
   }
 
@@ -33,6 +33,7 @@
 
   public final class AnimationClocksKt {
     method @androidx.compose.ui.test.ExperimentalTesting public static androidx.compose.animation.core.MonotonicFrameAnimationClock monotonicFrameAnimationClockOf(kotlin.coroutines.CoroutineContext coroutineContext, androidx.compose.runtime.dispatch.MonotonicFrameClock clock);
+    method @androidx.compose.ui.test.ExperimentalTesting public static androidx.compose.animation.core.MonotonicFrameAnimationClock monotonicFrameAnimationClockOf(kotlin.coroutines.CoroutineContext coroutineContext);
   }
 
   public final class AssertionsKt {
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidBitmapHelpers.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidBitmapHelpers.kt
index 6e27460..4060bdc 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidBitmapHelpers.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidBitmapHelpers.kt
@@ -25,13 +25,13 @@
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.addOutline
 import androidx.compose.ui.graphics.asAndroidPath
-import androidx.compose.ui.graphics.asImageAsset
+import androidx.compose.ui.graphics.asImageBitmap
 import androidx.compose.ui.node.ExperimentalLayoutNodeApi
 import androidx.compose.ui.platform.AndroidOwner
 import androidx.compose.ui.semantics.SemanticsProperties
@@ -55,8 +55,8 @@
  */
 @Suppress("DEPRECATION")
 @RequiresApi(Build.VERSION_CODES.O)
-fun SemanticsNodeInteraction.captureToImage(): ImageAsset =
-    captureToBitmap().asImageAsset()
+fun SemanticsNodeInteraction.captureToImage(): ImageBitmap =
+    captureToBitmap().asImageBitmap()
 
 /**
  * Captures the underlying semantics node's surface into bitmap.
@@ -148,7 +148,7 @@
  */
 @Suppress("DEPRECATION")
 @RequiresApi(Build.VERSION_CODES.O)
-fun View.captureToImage(): ImageAsset = captureToBitmap().asImageAsset()
+fun View.captureToImage(): ImageBitmap = captureToBitmap().asImageBitmap()
 
 /**
  * Captures the underlying view's surface into bitmap.
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/CoroutineBuilders.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/CoroutineBuilders.kt
index 50d732d..4a03271 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/CoroutineBuilders.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/CoroutineBuilders.kt
@@ -21,7 +21,7 @@
 import androidx.compose.animation.core.advanceClockMillis
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withContext
+import kotlinx.coroutines.yield
 
 /**
  * Runs a new coroutine and blocks the current thread interruptibly until it completes, passing a
@@ -75,15 +75,15 @@
 fun <R> runBlockingWithManualClock(
     compatibleWithManualAnimationClock: Boolean = false,
     block: suspend CoroutineScope.(clock: ManualFrameClock) -> R
-) = runBlocking {
+) {
     @Suppress("DEPRECATION")
     val clock = ManualFrameClock(0L, compatibleWithManualAnimationClock)
-    block(clock)
-    while (clock.hasAwaiters) {
-        withContext(TestUiDispatcher.Main) {
+    return runBlocking(clock) {
+        block(clock)
+        while (clock.hasAwaiters) {
             clock.advanceClockMillis(10_000L)
+            // Give awaiters the chance to await again
+            yield()
         }
-        // Give awaiters the chance to await again
-        withContext(TestUiDispatcher.Main) {}
     }
-}
+}
\ No newline at end of file
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/AnimationClocks.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/AnimationClocks.kt
index e4039ed..11ea390 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/AnimationClocks.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/AnimationClocks.kt
@@ -78,5 +78,19 @@
     clock: MonotonicFrameClock
 ): MonotonicFrameAnimationClock =
     MonotonicFrameAnimationClock(
-        CoroutineScope(coroutineContext + TestUiDispatcher.Main + clock)
+        CoroutineScope(coroutineContext + clock)
+    )
+
+/**
+ * Creates a [MonotonicFrameAnimationClock] from the given [coroutineContext]'s clock. A new
+ * coroutine scope is created from the [coroutineContext].
+ *
+ * @see MonotonicFrameAnimationClock
+ */
+@ExperimentalTesting
+fun monotonicFrameAnimationClockOf(
+    coroutineContext: CoroutineContext
+): MonotonicFrameAnimationClock =
+    MonotonicFrameAnimationClock(
+        CoroutineScope(coroutineContext)
     )
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
index 65cbb2c..f499d3a 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
@@ -19,8 +19,8 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageAsset
-import androidx.compose.ui.graphics.ImageAssetConfig
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.ImageBitmapConfig
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.PathOperation
 import androidx.compose.ui.graphics.Shadow
@@ -1874,16 +1874,16 @@
             // Make sure the maxLine is applied correctly
             assertThat(paragraphNoMaxLine.height).isGreaterThan(paragraphWithMaxLine.height)
 
-            val imageNoMaxLine = ImageAsset(
+            val imageNoMaxLine = ImageBitmap(
                 paragraphNoMaxLine.width.roundToInt(),
                 paragraphNoMaxLine.height.roundToInt(),
-                ImageAssetConfig.Argb8888
+                ImageBitmapConfig.Argb8888
             )
             // Same size with imageNoMaxLine for comparison
-            val imageWithMaxLine = ImageAsset(
+            val imageWithMaxLine = ImageBitmap(
                 paragraphNoMaxLine.width.roundToInt(),
                 paragraphNoMaxLine.height.roundToInt(),
-                ImageAssetConfig.Argb8888
+                ImageBitmapConfig.Argb8888
             )
 
             paragraphNoMaxLine.paint(Canvas(imageNoMaxLine))
diff --git a/compose/ui/ui-tooling/src/androidTest/AndroidManifest.xml b/compose/ui/ui-tooling/src/androidTest/AndroidManifest.xml
index 9740903..faa9b77 100644
--- a/compose/ui/ui-tooling/src/androidTest/AndroidManifest.xml
+++ b/compose/ui/ui-tooling/src/androidTest/AndroidManifest.xml
@@ -16,12 +16,12 @@
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    package="androidx.compose.ui.tooling.test">
+    package="androidx.compose.ui.tooling">
     <application
         android:debuggable="true"
         tools:ignore="HardcodedDebugMode"
         tools:replace="android:debuggable">
-        <activity android:name="androidx.compose.ui.tooling.ComposeViewAdapterTest$Companion$TestActivity"
+        <activity android:name="androidx.compose.ui.tooling.preview.ComposeViewAdapterTest$Companion$TestActivity"
             android:theme="@style/TestTheme"/>
         <activity android:name="androidx.compose.ui.tooling.TestActivity"
             android:theme="@style/TestTheme"/>
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/ComposeViewAdapterTest.kt
similarity index 97%
rename from compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt
rename to compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/ComposeViewAdapterTest.kt
index 135fc39..79d7ae1 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/ComposeViewAdapterTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.tooling
+package androidx.compose.ui.tooling.preview
 
 import android.app.Activity
 import android.os.Bundle
 import android.view.ViewTreeObserver
 import androidx.compose.animation.core.InternalAnimationApi
 import androidx.compose.animation.core.TransitionAnimation
-import androidx.compose.ui.tooling.preview.ComposeViewAdapter
-import androidx.compose.ui.tooling.preview.ViewInfo
+import androidx.compose.ui.tooling.compositionCount
 import androidx.compose.ui.tooling.preview.animation.PreviewAnimationClock
 import androidx.compose.ui.tooling.test.R
 import org.junit.Assert.assertArrayEquals
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/PreviewActivityTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/PreviewActivityTest.kt
similarity index 94%
rename from compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/PreviewActivityTest.kt
rename to compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/PreviewActivityTest.kt
index 24768cf..fba78f7 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/PreviewActivityTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/PreviewActivityTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.compose.ui.tooling
+package androidx.compose.ui.tooling.preview
 
 import android.content.Intent
 import android.view.ViewGroup
-import androidx.compose.ui.tooling.preview.PreviewActivity
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Rule
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/PreviewParameterTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/PreviewParameterTest.kt
index 8a89c33..c74b740 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/PreviewParameterTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/preview/PreviewParameterTest.kt
@@ -19,7 +19,6 @@
 import androidx.compose.material.Colors
 import androidx.compose.material.darkColors
 import androidx.compose.material.lightColors
-import androidx.compose.ui.tooling.ComposeViewAdapterTest
 import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
 import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
 import androidx.compose.ui.tooling.test.R
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index d485aa7..cd671d4 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -1773,7 +1773,7 @@
     method protected final long getMeasuredSize-YbymL2g();
     method protected final long getMeasurementConstraints-msEJaDk();
     method public final int getWidth();
-    method protected abstract void placeAt--gyyYBs(long position);
+    method protected abstract void placeAt-iGa6aRc(long position, float zIndex);
     method protected final void setMeasuredSize-ozmzZPI(long value);
     method protected final void setMeasurementConstraints-BRTryo0(long p);
     property protected final long apparentToRealOffset;
@@ -1787,12 +1787,12 @@
     ctor public Placeable.PlacementScope();
     method protected abstract androidx.compose.ui.unit.LayoutDirection getParentLayoutDirection();
     method protected abstract int getParentWidth();
-    method public final void place(androidx.compose.ui.layout.Placeable, int x, int y);
-    method public final void place-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
-    method public final void place-KmjayG8(androidx.compose.ui.layout.Placeable, long position);
-    method public final void placeRelative(androidx.compose.ui.layout.Placeable, int x, int y);
-    method public final void placeRelative-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
-    method public final void placeRelative-KmjayG8(androidx.compose.ui.layout.Placeable, long position);
+    method public final void place(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
+    method @Deprecated public final void place-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
+    method public final void place-z2mxYZE(androidx.compose.ui.layout.Placeable, long position, optional float zIndex);
+    method public final void placeRelative(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
+    method @Deprecated public final void placeRelative-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
+    method public final void placeRelative-z2mxYZE(androidx.compose.ui.layout.Placeable, long position, optional float zIndex);
     property protected abstract androidx.compose.ui.unit.LayoutDirection parentLayoutDirection;
     property protected abstract int parentWidth;
   }
@@ -2308,8 +2308,8 @@
   }
 
   public final class ImageResourcesKt {
-    method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.ImageAsset imageResource(@DrawableRes int id);
-    method @androidx.compose.runtime.Composable public static androidx.compose.ui.res.DeferredResource<androidx.compose.ui.graphics.ImageAsset> loadImageResource(int id, optional androidx.compose.ui.graphics.ImageAsset? pendingImage, optional androidx.compose.ui.graphics.ImageAsset? failedImage);
+    method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.ImageBitmap imageResource(@DrawableRes int id);
+    method @androidx.compose.runtime.Composable public static androidx.compose.ui.res.DeferredResource<androidx.compose.ui.graphics.ImageBitmap> loadImageResource(int id, optional androidx.compose.ui.graphics.ImageBitmap? pendingImage, optional androidx.compose.ui.graphics.ImageBitmap? failedImage);
   }
 
   public final class LoadedResource<T> extends androidx.compose.ui.res.Resource<T> {
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index d485aa7..cd671d4 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -1773,7 +1773,7 @@
     method protected final long getMeasuredSize-YbymL2g();
     method protected final long getMeasurementConstraints-msEJaDk();
     method public final int getWidth();
-    method protected abstract void placeAt--gyyYBs(long position);
+    method protected abstract void placeAt-iGa6aRc(long position, float zIndex);
     method protected final void setMeasuredSize-ozmzZPI(long value);
     method protected final void setMeasurementConstraints-BRTryo0(long p);
     property protected final long apparentToRealOffset;
@@ -1787,12 +1787,12 @@
     ctor public Placeable.PlacementScope();
     method protected abstract androidx.compose.ui.unit.LayoutDirection getParentLayoutDirection();
     method protected abstract int getParentWidth();
-    method public final void place(androidx.compose.ui.layout.Placeable, int x, int y);
-    method public final void place-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
-    method public final void place-KmjayG8(androidx.compose.ui.layout.Placeable, long position);
-    method public final void placeRelative(androidx.compose.ui.layout.Placeable, int x, int y);
-    method public final void placeRelative-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
-    method public final void placeRelative-KmjayG8(androidx.compose.ui.layout.Placeable, long position);
+    method public final void place(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
+    method @Deprecated public final void place-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
+    method public final void place-z2mxYZE(androidx.compose.ui.layout.Placeable, long position, optional float zIndex);
+    method public final void placeRelative(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
+    method @Deprecated public final void placeRelative-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
+    method public final void placeRelative-z2mxYZE(androidx.compose.ui.layout.Placeable, long position, optional float zIndex);
     property protected abstract androidx.compose.ui.unit.LayoutDirection parentLayoutDirection;
     property protected abstract int parentWidth;
   }
@@ -2308,8 +2308,8 @@
   }
 
   public final class ImageResourcesKt {
-    method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.ImageAsset imageResource(@DrawableRes int id);
-    method @androidx.compose.runtime.Composable public static androidx.compose.ui.res.DeferredResource<androidx.compose.ui.graphics.ImageAsset> loadImageResource(int id, optional androidx.compose.ui.graphics.ImageAsset? pendingImage, optional androidx.compose.ui.graphics.ImageAsset? failedImage);
+    method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.ImageBitmap imageResource(@DrawableRes int id);
+    method @androidx.compose.runtime.Composable public static androidx.compose.ui.res.DeferredResource<androidx.compose.ui.graphics.ImageBitmap> loadImageResource(int id, optional androidx.compose.ui.graphics.ImageBitmap? pendingImage, optional androidx.compose.ui.graphics.ImageBitmap? failedImage);
   }
 
   public final class LoadedResource<T> extends androidx.compose.ui.res.Resource<T> {
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index b87c61e..ecb8dd6 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -1820,7 +1820,7 @@
     method protected final long getMeasuredSize-YbymL2g();
     method protected final long getMeasurementConstraints-msEJaDk();
     method public final int getWidth();
-    method protected abstract void placeAt--gyyYBs(long position);
+    method protected abstract void placeAt-iGa6aRc(long position, float zIndex);
     method protected final void setMeasuredSize-ozmzZPI(long value);
     method protected final void setMeasurementConstraints-BRTryo0(long p);
     property protected final long apparentToRealOffset;
@@ -1834,12 +1834,12 @@
     ctor public Placeable.PlacementScope();
     method protected abstract androidx.compose.ui.unit.LayoutDirection getParentLayoutDirection();
     method protected abstract int getParentWidth();
-    method public final void place(androidx.compose.ui.layout.Placeable, int x, int y);
-    method public final void place-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
-    method public final void place-KmjayG8(androidx.compose.ui.layout.Placeable, long position);
-    method public final void placeRelative(androidx.compose.ui.layout.Placeable, int x, int y);
-    method public final void placeRelative-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
-    method public final void placeRelative-KmjayG8(androidx.compose.ui.layout.Placeable, long position);
+    method public final void place(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
+    method @Deprecated public final void place-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
+    method public final void place-z2mxYZE(androidx.compose.ui.layout.Placeable, long position, optional float zIndex);
+    method public final void placeRelative(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
+    method @Deprecated public final void placeRelative-I1SjWws(androidx.compose.ui.layout.Placeable, long position);
+    method public final void placeRelative-z2mxYZE(androidx.compose.ui.layout.Placeable, long position, optional float zIndex);
     property protected abstract androidx.compose.ui.unit.LayoutDirection parentLayoutDirection;
     property protected abstract int parentWidth;
   }
@@ -2385,8 +2385,8 @@
   }
 
   public final class ImageResourcesKt {
-    method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.ImageAsset imageResource(@DrawableRes int id);
-    method @androidx.compose.runtime.Composable public static androidx.compose.ui.res.DeferredResource<androidx.compose.ui.graphics.ImageAsset> loadImageResource(int id, optional androidx.compose.ui.graphics.ImageAsset? pendingImage, optional androidx.compose.ui.graphics.ImageAsset? failedImage);
+    method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.ImageBitmap imageResource(@DrawableRes int id);
+    method @androidx.compose.runtime.Composable public static androidx.compose.ui.res.DeferredResource<androidx.compose.ui.graphics.ImageBitmap> loadImageResource(int id, optional androidx.compose.ui.graphics.ImageBitmap? pendingImage, optional androidx.compose.ui.graphics.ImageBitmap? failedImage);
   }
 
   public final class LoadedResource<T> extends androidx.compose.ui.res.Resource<T> {
diff --git a/compose/ui/ui/integration-tests/ui-demos/lint-baseline.xml b/compose/ui/ui/integration-tests/ui-demos/lint-baseline.xml
index a3129e5..aedc407 100644
--- a/compose/ui/ui/integration-tests/ui-demos/lint-baseline.xml
+++ b/compose/ui/ui/integration-tests/ui-demos/lint-baseline.xml
@@ -24,17 +24,6 @@
     </issue>
 
     <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="    composed {"
-        errorLine2="    ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/ui/demos/gestures/PointerInputDuringSubCompDemo.kt"
-            line="92"
-            column="5"/>
-    </issue>
-
-    <issue
         id="SetTextI18n"
         message="String literal in `setText` can not be translated. Use Android resources instead."
         errorLine1="            text1.text = &quot;Text updated&quot;"
diff --git a/compose/ui/ui/lint-baseline.xml b/compose/ui/ui/lint-baseline.xml
index aad399c..2595fa3 100644
--- a/compose/ui/ui/lint-baseline.xml
+++ b/compose/ui/ui/lint-baseline.xml
@@ -686,83 +686,6 @@
     <issue
         id="ModifierInspectorInfo"
         message="Modifier missing inspectorInfo"
-        errorLine1=") = if (elevation > 0.dp || clip) {"
-        errorLine2="    ^">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/draw/DrawShadow.kt"
-            line="50"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="    return this.then(FocusObserverModifierImpl(onFocusChange))"
-        errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/FocusObserverModifier.kt"
-            line="43"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="    return this.then(FocusRequesterModifierImpl(focusRequester))"
-        errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/FocusRequesterModifier.kt"
-            line="46"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="fun Modifier.keyInputFilter(onKeyEvent: (KeyEvent) -> Boolean): Modifier = composed {"
-        errorLine2="                                                                           ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt"
-            line="32"
-            column="76"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="fun Modifier.previewKeyInputFilter(onPreviewKeyEvent: (KeyEvent) -> Boolean): Modifier = composed {"
-        errorLine2="                                                                                         ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt"
-            line="47"
-            column="90"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilter.kt"
-            line="121"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/LongPressGestureFilter.kt"
-            line="58"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
         errorLine1=") = this.then(object : OnGloballyPositionedModifier {"
         errorLine2="              ^">
         <location
@@ -788,94 +711,6 @@
         errorLine1="): Modifier = composed {"
         errorLine2="              ~~~~~~~~">
         <location
-            file="src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilter.kt"
-            line="62"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/PressIndicatorGestureFilter.kt"
-            line="57"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/RawDragGestureFilter.kt"
-            line="153"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/RawPressStartGestureFilter.kt"
-            line="57"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/RawScaleGestureFilter.kt"
-            line="122"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/ScaleGestureFilter.kt"
-            line="106"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/ScaleSlopExceededGestureFilter.kt"
-            line="51"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/gesture/ScrollGestureFilter.kt"
-            line="120"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="ModifierInspectorInfo"
-        message="Modifier missing inspectorInfo"
-        errorLine1="): Modifier = composed {"
-        errorLine2="              ~~~~~~~~">
-        <location
             file="src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt"
             line="85"
             column="15"/>
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
index e3e2fb1..f02a9de 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
@@ -54,7 +54,7 @@
 import androidx.compose.ui.graphics.Outline
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.asImageAsset
+import androidx.compose.ui.graphics.asImageBitmap
 import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.graphics.drawscope.clipRect
 import androidx.compose.ui.graphics.drawscope.translate
@@ -91,12 +91,14 @@
 import androidx.compose.ui.test.TestActivity
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.constrainHeight
 import androidx.compose.ui.unit.constrainWidth
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.offset
+import androidx.compose.ui.unit.toOffset
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
@@ -2200,7 +2202,7 @@
     @Test
     fun layoutModifier_convenienceApi() {
         val size = 100
-        val offset = 15f
+        val offset = 15
         val latch = CountDownLatch(1)
         var resultCoordinates: LayoutCoordinates? = null
 
@@ -2212,7 +2214,7 @@
                         .layout { measurable, constraints ->
                             val placeable = measurable.measure(constraints)
                             layout(placeable.width, placeable.height) {
-                                placeable.place(Offset(offset, offset))
+                                placeable.place(offset, offset)
                             }
                         }.onGloballyPositioned {
                             resultCoordinates = it
@@ -2227,14 +2229,14 @@
         activity.runOnUiThread {
             assertEquals(size, resultCoordinates?.size?.height)
             assertEquals(size, resultCoordinates?.size?.width)
-            assertEquals(Offset(offset, offset), resultCoordinates?.positionInRoot)
+            assertEquals(IntOffset(offset, offset).toOffset(), resultCoordinates?.positionInRoot)
         }
     }
 
     @Test
     fun layoutModifier_convenienceApi_equivalent() {
         val size = 100
-        val offset = 15f
+        val offset = 15
         val latch = CountDownLatch(2)
 
         var convenienceCoordinates: LayoutCoordinates? = null
@@ -2248,7 +2250,7 @@
                         .layout { measurable, constraints ->
                             val placeable = measurable.measure(constraints)
                             layout(placeable.width, placeable.height) {
-                                placeable.place(Offset(offset, offset))
+                                placeable.place(offset, offset)
                             }
                         }.onGloballyPositioned {
                             convenienceCoordinates = it
@@ -2263,7 +2265,7 @@
                     ): MeasureResult {
                         val placeable = measurable.measure(constraints)
                         return layout(placeable.width, placeable.height) {
-                            placeable.place(Offset(offset, offset))
+                            placeable.place(offset, offset)
                         }
                     }
                 }
@@ -3180,7 +3182,7 @@
             )
         }
 
-        activityTestRule.waitAndScreenShot(frameLayout).asImageAsset()
+        activityTestRule.waitAndScreenShot(frameLayout).asImageBitmap()
             .assertPixels(expectedSize = IntSize(size, size)) {
                 Color.Red
             }
@@ -3375,7 +3377,7 @@
     offset: Int = 0,
     totalSize: Int = size * 3
 ) {
-    assertTrue("drawLatch timed out", drawLatch.await(10000, TimeUnit.SECONDS))
+    assertTrue("drawLatch timed out", drawLatch.await(1, TimeUnit.SECONDS))
     val bitmap = waitAndScreenShot()
     assertEquals(totalSize, bitmap.width)
     assertEquals(totalSize, bitmap.height)
@@ -3694,6 +3696,8 @@
 
 fun PaddingModifier(padding: Int) = PaddingModifier(padding, padding, padding, padding)
 
+fun Modifier.padding(padding: Int) = this.then(PaddingModifier(padding))
+
 data class PaddingModifier(
     val left: Int = 0,
     val top: Int = 0,
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt
index 44c7ffb..a8034fc 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt
@@ -23,18 +23,20 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.emptyContent
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.AtLeastSize
 import androidx.compose.ui.DrawLayerModifier
 import androidx.compose.ui.FixedSize
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.PaddingModifier
-import androidx.compose.ui.ZIndexModifier
 import androidx.compose.ui.background
 import androidx.compose.ui.drawBehind
 import androidx.compose.ui.drawLayer
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.padding
 import androidx.compose.ui.platform.AndroidOwnerExtraAssertionsRule
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.test.TestActivity
@@ -44,7 +46,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
-import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertTrue
 import org.junit.Before
@@ -84,7 +85,7 @@
                     children = {
                         FixedSize(
                             10,
-                            PaddingModifier(10)
+                            Modifier.padding(10)
                                 .background(Color.White)
                         )
                         FixedSize(
@@ -124,7 +125,7 @@
                     children = {
                         FixedSize(
                             10,
-                            PaddingModifier(10)
+                            Modifier.padding(10)
                                 .background(Color.White)
                         )
                         FixedSize(
@@ -157,14 +158,14 @@
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun testDrawingOrderIsOverriddenWithZIndexWhenWePlaceItemsInTheReverseOrder() {
+    fun testDrawingOrderIsOverriddenWithZIndexModifierWhenWePlaceItemsInTheReverseOrder() {
         rule.runOnUiThread {
             activity.setContent {
                 Layout(
                     children = {
                         FixedSize(
                             10,
-                            PaddingModifier(10)
+                            Modifier.padding(10)
                                 .background(Color.White)
                         )
                         FixedSize(
@@ -198,6 +199,47 @@
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    fun testDrawingOrderIsOverriddenWithZIndexWhenWePlaceItemsInTheReverseOrder() {
+        rule.runOnUiThread {
+            activity.setContent {
+                Layout(
+                    children = {
+                        FixedSize(
+                            10,
+                            Modifier.padding(10)
+                                .background(Color.White)
+                        )
+                        FixedSize(
+                            30,
+                            Modifier.drawLayer()
+                                .background(Color.Red)
+                                .zIndex(1f)
+                                .drawLatchModifier()
+                        )
+                    }
+                ) { measurables, _ ->
+                    val newConstraints = Constraints.fixed(30, 30)
+                    val placeables = measurables.map { m ->
+                        m.measure(newConstraints)
+                    }
+                    layout(newConstraints.maxWidth, newConstraints.maxWidth) {
+                        placeables.reversed().forEach { child ->
+                            child.place(0, 0, zIndex = placeables.indexOf(child).toFloat())
+                        }
+                    }
+                }
+            }
+        }
+        rule.validateSquareColors(
+            outerColor = Color.Red,
+            innerColor = Color.Red,
+            size = 10,
+            drawLatch = drawLatch
+        )
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun testCustomDrawingOrderForThreeItems() {
         rule.runOnUiThread {
             activity.setContent {
@@ -211,7 +253,7 @@
                         )
                         FixedSize(
                             10,
-                            PaddingModifier(10)
+                            Modifier.padding(10)
                                 .background(Color.White)
                         )
                         FixedSize(
@@ -281,7 +323,7 @@
                 ) {
                     FixedSize(
                         10,
-                        PaddingModifier(10)
+                        Modifier.padding(10)
                             .zIndex(1f)
                             .background(Color.White)
                     )
@@ -312,7 +354,7 @@
                 ) {
                     FixedSize(
                         10,
-                        PaddingModifier(10)
+                        Modifier.padding(10)
                             .zIndex(1f)
                             .background(Color.White)
                     )
@@ -340,7 +382,7 @@
                 FixedSize(
                     size = 30
                 ) {
-                    FixedSize(10, PaddingModifier(10)) {
+                    FixedSize(10, Modifier.padding(10)) {
                         FixedSize(
                             10,
                             Modifier.zIndex(1f)
@@ -348,7 +390,7 @@
                         )
                     }
                     FixedSize(30, Modifier.background(Color.Red))
-                    FixedSize(10, PaddingModifier(10)) {
+                    FixedSize(10, Modifier.padding(10)) {
                         FixedSize(
                             10,
                             Modifier.background(Color.White)
@@ -374,7 +416,7 @@
                 FixedSize(
                     size = 30
                 ) {
-                    FixedSize(10, PaddingModifier(10)) {
+                    FixedSize(10, Modifier.padding(10)) {
                         FixedSize(
                             10,
                             Modifier.zIndex(1f)
@@ -409,7 +451,7 @@
                 ) {
                     FixedSize(
                         10,
-                        PaddingModifier(10)
+                        Modifier.padding(10)
                             .zIndex(state.value)
                             .background(Color.Black)
                     )
@@ -420,7 +462,7 @@
                     )
                     FixedSize(
                         10,
-                        PaddingModifier(10).background(Color.White)
+                        Modifier.padding(10).background(Color.White)
                     )
                 }
             }
@@ -469,11 +511,13 @@
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun testChangingZOrderReusingModifiers() {
         val state = mutableStateOf(0f)
-        val zIndex = object : ZIndexModifier {
-            override val zIndex: Float
-                get() = state.value
+        val zIndex = Modifier.layout { measurable, constraints ->
+            val placeable = measurable.measure(constraints)
+            layout(placeable.width, placeable.height) {
+                placeable.place(0, 0, zIndex = state.value)
+            }
         }
-        val modifier1 = PaddingModifier(10)
+        val modifier1 = Modifier.padding(10)
             .then(zIndex)
             .background(Color.White)
         val modifier2 = Modifier.background(Color.Red)
@@ -545,7 +589,7 @@
                     FixedSize(30) {
                         FixedSize(
                             10,
-                            PaddingModifier(10).then(elevation).background(Color.Black)
+                            Modifier.padding(10).then(elevation).background(Color.Black)
                         )
                     }
                     FixedSize(
@@ -555,7 +599,7 @@
                     )
                     FixedSize(
                         10,
-                        PaddingModifier(10)
+                        Modifier.padding(10)
                             .background(Color.White)
                     )
                 }
@@ -601,7 +645,7 @@
                 ) {
                     FixedSize(
                         size,
-                        PaddingModifier(10)
+                        Modifier.padding(10)
                             .zIndex(1f)
                             .background(Color.White)
                     )
@@ -650,7 +694,7 @@
                 FixedSize(size = 30) {
                     FixedSize(
                         10,
-                        PaddingModifier(10)
+                        Modifier.padding(10)
                             .zIndex(1f)
                             .background(Color.White)
                     )
@@ -690,7 +734,7 @@
                 ) {
                     FixedSize(
                         10,
-                        PaddingModifier(10)
+                        Modifier.padding(10)
                             .zIndex(2f)
                             .zIndex(2f)
                             .background(Color.White)
@@ -714,77 +758,6 @@
     }
 
     @Test
-    fun elevationWithinModifier() {
-        val elevation = mutableStateOf(0f)
-        val color = mutableStateOf(Color.Blue)
-        val underColor = mutableStateOf(Color.Transparent)
-        val modifier = Modifier.drawLayer()
-            .background(underColor)
-            .drawLatchModifier()
-            .then(object : DrawLayerModifier {
-                override val shadowElevation: Float
-                    get() {
-                        return elevation.value
-                    }
-            })
-            .background(color)
-
-        rule.runOnUiThread {
-            activity.setContent {
-                FixedSize(30, modifier)
-            }
-        }
-
-        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
-
-        drawLatch = CountDownLatch(1)
-
-        rule.runOnUiThread {
-            color.value = Color.Red
-        }
-
-        assertFalse(drawLatch.await(200, TimeUnit.MILLISECONDS))
-
-        drawLatch = CountDownLatch(1)
-        rule.runOnUiThread {
-            elevation.value = 1f
-        }
-
-        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
-
-        drawLatch = CountDownLatch(1)
-
-        rule.runOnUiThread {
-            elevation.value = 2f // elevation was already 1, so it doesn't need to enableZ again
-        }
-        assertFalse(drawLatch.await(200, TimeUnit.MILLISECONDS))
-
-        rule.runOnUiThread {
-            elevation.value = 0f // going to 0 doesn't trigger invalidation
-        }
-        assertFalse(drawLatch.await(200, TimeUnit.MILLISECONDS))
-
-        rule.runOnUiThread {
-            elevation.value = 1f // going to 1 won't invalidate because it was last drawn with Z
-        }
-        assertFalse(drawLatch.await(200, TimeUnit.MILLISECONDS))
-
-        rule.runOnUiThread {
-            elevation.value = 0f
-            underColor.value = Color.Black
-        }
-
-        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
-
-        drawLatch = CountDownLatch(1)
-
-        rule.runOnUiThread {
-            elevation.value = 1f
-        }
-        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
-    }
-
-    @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun testInvalidateParentOfReorderedChild() {
         val color = mutableStateOf(Color.Red)
@@ -793,7 +766,7 @@
                 FixedSize(size = 30) {
                     FixedSize(
                         10,
-                        PaddingModifier(10)
+                        Modifier.padding(10)
                             .zIndex(1f)
                             .background(Color.White)
                     )
@@ -829,7 +802,7 @@
                 ) {
                     FixedSize(
                         10,
-                        PaddingModifier(10)
+                        Modifier.padding(10)
                             .drawLayer(shadowElevation = 1f)
                             .background(Color.White)
                     )
@@ -850,15 +823,226 @@
         )
     }
 
-    fun Modifier.drawLatchModifier() = drawBehind { drawLatch.countDown() }
-}
-
-private fun Modifier.background(
-    color: State<Color>
-) = drawBehind {
-    if (color.value != Color.Transparent) {
-        drawRect(color.value)
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    fun placeOrderIsUsedWhenParentProvidedSameZIndex() {
+        rule.runOnUiThread {
+            activity.setContent {
+                Layout(
+                    children = {
+                        FixedSize(30) {
+                            FixedSize(
+                                10,
+                                Modifier.padding(10)
+                                    .background(Color.White)
+                            )
+                        }
+                        FixedSize(30) {
+                            FixedSize(
+                                30,
+                                Modifier.background(Color.Red)
+                                    .drawLatchModifier()
+                            )
+                        }
+                    }
+                ) { measurables, _ ->
+                    val newConstraints = Constraints.fixed(30, 30)
+                    val placeables = measurables.map { m ->
+                        m.measure(newConstraints)
+                    }
+                    layout(newConstraints.maxWidth, newConstraints.maxWidth) {
+                        placeables[0].place(0, 0, zIndex = 1f)
+                        placeables[1].place(0, 0, zIndex = 1f)
+                    }
+                }
+            }
+        }
+        rule.validateSquareColors(
+            outerColor = Color.Red,
+            innerColor = Color.Red,
+            size = 10,
+            drawLatch = drawLatch
+        )
     }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    fun placeOrderIsUsedWhenParentProvidedSameZIndex_reversePlaceOrder() {
+        rule.runOnUiThread {
+            activity.setContent {
+                Layout(
+                    children = {
+                        FixedSize(30) {
+                            FixedSize(
+                                10,
+                                Modifier.padding(10)
+                                    .background(Color.White)
+                            )
+                        }
+                        FixedSize(30) {
+                            FixedSize(
+                                30,
+                                Modifier.background(Color.Red)
+                                    .drawLatchModifier()
+                            )
+                        }
+                    }
+                ) { measurables, _ ->
+                    val newConstraints = Constraints.fixed(30, 30)
+                    val placeables = measurables.map { m ->
+                        m.measure(newConstraints)
+                    }
+                    layout(newConstraints.maxWidth, newConstraints.maxWidth) {
+                        placeables[1].place(0, 0, zIndex = 1f)
+                        placeables[0].place(0, 0, zIndex = 1f)
+                    }
+                }
+            }
+        }
+        rule.validateSquareColors(
+            outerColor = Color.Red,
+            innerColor = Color.White,
+            size = 10,
+            drawLatch = drawLatch
+        )
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    fun parentProvidedZIndexSummedWithTheOneFromModifier() {
+        rule.runOnUiThread {
+            activity.setContent {
+                Layout(
+                    children = {
+                        FixedSize(30, Modifier.zIndex(2f)) {
+                            FixedSize(
+                                10,
+                                Modifier.padding(10)
+                                    .background(Color.White)
+                            )
+                        }
+                        FixedSize(30) {
+                            FixedSize(
+                                30,
+                                Modifier.background(Color.Red)
+                                    .drawLatchModifier()
+                            )
+                        }
+                    }
+                ) { measurables, _ ->
+                    val newConstraints = Constraints.fixed(30, 30)
+                    val placeables = measurables.map { m ->
+                        m.measure(newConstraints)
+                    }
+                    layout(newConstraints.maxWidth, newConstraints.maxWidth) {
+                        placeables[0].place(0, 0, zIndex = 1f)
+                        placeables[1].place(0, 0, zIndex = 2f)
+                    }
+                }
+            }
+        }
+        rule.validateSquareColors(
+            outerColor = Color.Red,
+            innerColor = Color.White,
+            size = 10,
+            drawLatch = drawLatch
+        )
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    fun placeRelativePassesZIndex() {
+        rule.runOnUiThread {
+            activity.setContent {
+                Layout(
+                    children = {
+                        FixedSize(30) {
+                            FixedSize(
+                                10,
+                                Modifier.padding(10)
+                                    .background(Color.White)
+                            )
+                        }
+                        FixedSize(30) {
+                            FixedSize(
+                                30,
+                                Modifier.background(Color.Red)
+                                    .drawLatchModifier()
+                            )
+                        }
+                    }
+                ) { measurables, _ ->
+                    val newConstraints = Constraints.fixed(30, 30)
+                    val placeables = measurables.map { m ->
+                        m.measure(newConstraints)
+                    }
+                    layout(newConstraints.maxWidth, newConstraints.maxWidth) {
+                        placeables[0].placeRelative(0, 0, zIndex = 1f)
+                        placeables[1].placeRelative(0, 0, zIndex = -1f)
+                    }
+                }
+            }
+        }
+        rule.validateSquareColors(
+            outerColor = Color.Red,
+            innerColor = Color.White,
+            size = 10,
+            drawLatch = drawLatch
+        )
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    fun whenSecondChildAddedLaterDrawingOrderIsStillCorrect() {
+        var needSecondChild by mutableStateOf(false)
+        rule.runOnUiThread {
+            activity.setContent {
+                Layout(
+                    children = {
+                        FixedSize(30) {
+                            FixedSize(
+                                10,
+                                Modifier.padding(10)
+                                    .background(Color.White)
+                                    .drawLatchModifier()
+                            )
+                        }
+                        if (needSecondChild) {
+                            FixedSize(30) {
+                                FixedSize(
+                                    30,
+                                    Modifier.background(Color.Red)
+                                )
+                            }
+                        }
+                    }
+                ) { measurables, _ ->
+                    val newConstraints = Constraints.fixed(30, 30)
+                    val placeables = measurables.map { m ->
+                        m.measure(newConstraints)
+                    }
+                    layout(newConstraints.maxWidth, newConstraints.maxWidth) {
+                        placeables[0].placeRelative(0, 0, zIndex = 1f)
+                        placeables.getOrNull(1)?.placeRelative(0, 0)
+                    }
+                }
+            }
+        }
+        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
+        rule.runOnUiThread {
+            drawLatch = CountDownLatch(1)
+            needSecondChild = true
+        }
+
+        rule.validateSquareColors(
+            outerColor = Color.Red,
+            innerColor = Color.White,
+            size = 10,
+            drawLatch = drawLatch
+        )
+    }
+
+    fun Modifier.drawLatchModifier() = drawBehind { drawLatch.countDown() }
 }
 
 @Composable
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawShadowTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawShadowTest.kt
index 0bc2cec..66ec42a 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawShadowTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawShadowTest.kt
@@ -22,6 +22,7 @@
 import androidx.compose.runtime.State
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.AtLeastSize
+import androidx.compose.ui.DrawLayerModifier
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.drawBehind
 import androidx.compose.ui.drawLayer
@@ -29,9 +30,13 @@
 import androidx.compose.ui.geometry.toRect
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.luminance
 import androidx.compose.ui.platform.DensityAmbient
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.runOnUiThreadIR
 import androidx.compose.ui.test.TestActivity
@@ -42,6 +47,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assert
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertTrue
@@ -72,6 +80,12 @@
         activity = rule.activity
         activity.hasFocusLatch.await(5, TimeUnit.SECONDS)
         drawLatch = CountDownLatch(1)
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun tearDown() {
+        isDebugInspectorInfoEnabled = false
     }
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@@ -207,6 +221,91 @@
         }
     }
 
+    @Test
+    fun testInspectorValue() {
+        rule.runOnUiThreadIR {
+            val modifier = Modifier.drawShadow(4.0.dp) as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("drawShadow")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("elevation", 4.0.dp),
+                ValueElement("shape", RectangleShape),
+                ValueElement("clip", true)
+            )
+        }
+    }
+
+    @Test
+    fun elevationWithinModifier() {
+        val elevation = mutableStateOf(0f)
+        val color = mutableStateOf(Color.Blue)
+        val underColor = mutableStateOf(Color.Transparent)
+        val modifier = Modifier.drawLayer()
+            .background(underColor)
+            .drawLatchModifier()
+            .then(object : DrawLayerModifier {
+                override val shadowElevation: Float
+                    get() {
+                        return elevation.value
+                    }
+            })
+            .background(color)
+
+        rule.runOnUiThread {
+            activity.setContent {
+                androidx.compose.ui.FixedSize(30, modifier)
+            }
+        }
+
+        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
+
+        drawLatch = CountDownLatch(1)
+
+        rule.runOnUiThread {
+            color.value = Color.Red
+        }
+
+        Assert.assertFalse(drawLatch.await(200, TimeUnit.MILLISECONDS))
+
+        drawLatch = CountDownLatch(1)
+        rule.runOnUiThread {
+            elevation.value = 1f
+        }
+
+        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
+
+        drawLatch = CountDownLatch(1)
+
+        rule.runOnUiThread {
+            elevation.value = 2f // elevation was already 1, so it doesn't need to enableZ again
+        }
+        Assert.assertFalse(drawLatch.await(200, TimeUnit.MILLISECONDS))
+
+        rule.runOnUiThread {
+            elevation.value = 0f // going to 0 doesn't trigger invalidation
+        }
+        Assert.assertFalse(drawLatch.await(200, TimeUnit.MILLISECONDS))
+
+        rule.runOnUiThread {
+            elevation.value = 1f // going to 1 won't invalidate because it was last drawn with Z
+        }
+        Assert.assertFalse(drawLatch.await(200, TimeUnit.MILLISECONDS))
+
+        rule.runOnUiThread {
+            elevation.value = 0f
+            underColor.value = Color.Black
+        }
+
+        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
+
+        drawLatch = CountDownLatch(1)
+
+        rule.runOnUiThread {
+            elevation.value = 1f
+        }
+        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
+    }
+
     @Composable
     private fun ShadowContainer(
         elevation: State<Dp> = mutableStateOf(8.dp),
@@ -230,6 +329,16 @@
         drawLatch.countDown()
     }
 
+    fun Modifier.drawLatchModifier() = drawBehind { drawLatch.countDown() }
+
+    private fun Modifier.background(
+        color: State<Color>
+    ) = drawBehind {
+        if (color.value != Color.Transparent) {
+            drawRect(color.value)
+        }
+    }
+
     private fun takeScreenShot(width: Int, height: Int = width): Bitmap {
         val bitmap = rule.waitAndScreenShot()
         assertEquals(width, bitmap.width)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt
index b5ef674..09458a6 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt
@@ -41,11 +41,11 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
 import androidx.compose.ui.graphics.DefaultAlpha
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.asAndroidBitmap
-import androidx.compose.ui.graphics.asImageAsset
+import androidx.compose.ui.graphics.asImageBitmap
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.graphics.painter.ImagePainter
@@ -528,7 +528,7 @@
         // height and width centered within the bounds of the composable
         val boxWidth = 600
         val boxHeight = 400
-        val srcImage = ImageAsset(100, 200)
+        val srcImage = ImageBitmap(100, 200)
         val canvas = Canvas(srcImage)
         val paint = Paint().apply { this.color = Color.Red }
         canvas.drawRect(0f, 0f, 400f, 200f, paint)
@@ -568,12 +568,12 @@
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     @Test
     fun testImagePainterScalesNonUniformly() {
-        // The composable dimensions are larger than the ImageAsset. By not passing in
-        // a ContentScale parameter to the painter, the ImageAsset should be stretched
+        // The composable dimensions are larger than the ImageBitmap. By not passing in
+        // a ContentScale parameter to the painter, the ImageBitmap should be stretched
         // non-uniformly to fully occupy the bounds of the composable
         val boxWidth = 60
         val boxHeight = 40
-        val srcImage = ImageAsset(10, 20)
+        val srcImage = ImageBitmap(10, 20)
         val canvas = Canvas(srcImage)
         val paint = Paint().apply { this.color = Color.Red }
         canvas.drawRect(0f, 0f, 40f, 20f, paint)
@@ -591,7 +591,7 @@
             )
         }
 
-        rule.obtainScreenshotBitmap(boxWidth, boxHeight).asImageAsset().assertPixels { Color.Red }
+        rule.obtainScreenshotBitmap(boxWidth, boxHeight).asImageBitmap().assertPixels { Color.Red }
     }
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt
index 9cdd663..482bbac 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt
@@ -23,14 +23,19 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.test.TestActivity
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
 import com.nhaarman.mockitokotlin2.inOrder
 import com.nhaarman.mockitokotlin2.spy
 import com.nhaarman.mockitokotlin2.verify
 import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
+import org.junit.After
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Rule
@@ -94,6 +99,16 @@
         )
     }
 
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
     @Test
     fun ui_downMoveUpBeforeLongPressTimeout_noCallbacksCalled() {
 
@@ -383,6 +398,20 @@
         verifyNoMoreInteractions(longPressDragObserver)
     }
 
+    @Test
+    fun testInspectorValue() {
+        val onLongPress: () -> Unit = {}
+        val observer = MyLongPressDragObserver(onLongPress)
+        activityTestRule.runOnUiThreadIR {
+            val modifier = Modifier.longPressDragGestureFilter(observer) as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("longPressDragGestureFilter")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("longPressDragObserver", observer)
+            )
+        }
+    }
+
     private fun waitForLongPress(block: () -> Unit) {
         longPressCountDownLatch = CountDownLatch(1)
         activityTestRule.runOnUiThreadIR(block)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressGestureFilterComposeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressGestureFilterComposeTest.kt
new file mode 100644
index 0000000..508a0b2
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressGestureFilterComposeTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.gesture
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class LongPressGestureFilterComposeTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @Test
+    fun testInspectorValue() {
+        val onLongPress: (Offset) -> Unit = {}
+        rule.setContent {
+            val modifier = Modifier.longPressGestureFilter(onLongPress) as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("longPressGestureFilter")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("onLongPress", onLongPress)
+            )
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/PressIndicatorGestureFilterComposeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/PressIndicatorGestureFilterComposeTest.kt
new file mode 100644
index 0000000..18ed376
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/PressIndicatorGestureFilterComposeTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.gesture
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class PressIndicatorGestureFilterComposeTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @Test
+    fun testInspectorValue() {
+        val onStart: (Offset) -> Unit = {}
+        val onStop: () -> Unit = {}
+        val onCancel: () -> Unit = {}
+        val enabled = true
+        rule.setContent {
+            val modifier = Modifier.pressIndicatorGestureFilter(onStart, onStop, onCancel, enabled)
+                as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("pressIndicatorGestureFilter")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("onStart", onStart),
+                ValueElement("onStop", onStop),
+                ValueElement("onCancel", onCancel),
+                ValueElement("enabled", enabled)
+            )
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/RawDragGestureFilterComposeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/RawDragGestureFilterComposeTest.kt
new file mode 100644
index 0000000..1c831a9
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/RawDragGestureFilterComposeTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.gesture
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class RawDragGestureFilterComposeTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @Test
+    fun testInspectorValue() {
+        val dragObserver = object : DragObserver {}
+        val canStartDragging: () -> Boolean = { true }
+        rule.setContent {
+            val modifier = Modifier.rawDragGestureFilter(
+                dragObserver,
+                canStartDragging,
+                Orientation.Horizontal
+            ) as InspectableValue
+
+            assertThat(modifier.nameFallback).isEqualTo("rawDragGestureFilter")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("dragObserver", dragObserver),
+                ValueElement("canStartDragging", canStartDragging),
+                ValueElement("orientation", Orientation.Horizontal)
+            )
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/RawPressStartGestureFilterComposeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/RawPressStartGestureFilterComposeTest.kt
new file mode 100644
index 0000000..17cfbce
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/RawPressStartGestureFilterComposeTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.gesture
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class RawPressStartGestureFilterComposeTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @Test
+    fun testInspectorValue() {
+        val onPressStart: (Offset) -> Unit = {}
+        PointerEventPass.Main
+        rule.setContent {
+            val modifier = Modifier.rawPressStartGestureFilter(
+                onPressStart,
+                true,
+                PointerEventPass.Main
+            ) as InspectableValue
+
+            assertThat(modifier.nameFallback).isEqualTo("rawPressStartGestureFilter")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("onPressStart", onPressStart),
+                ValueElement("enabled", true),
+                ValueElement("executionPass", PointerEventPass.Main)
+            )
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/RawScaleGestureFilterComposeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/RawScaleGestureFilterComposeTest.kt
new file mode 100644
index 0000000..0b5dafc
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/RawScaleGestureFilterComposeTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.gesture
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class RawScaleGestureFilterComposeTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @Test
+    fun testInspectorValue() {
+        val observer = object : RawScaleObserver {}
+        val canStartScaling: () -> Boolean = { true }
+        rule.setContent {
+            val modifier = Modifier.rawScaleGestureFilter(observer, canStartScaling)
+                as InspectableValue
+
+            assertThat(modifier.nameFallback).isEqualTo("rawScaleGestureFilter")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("scaleObserver", observer),
+                ValueElement("canStartScaling", canStartScaling)
+            )
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/ScaleGestureFilterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/ScaleGestureFilterTest.kt
index a1c156c..5e7a49b 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/ScaleGestureFilterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/ScaleGestureFilterTest.kt
@@ -23,15 +23,20 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.DensityAmbient
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.test.TestActivity
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
 import com.nhaarman.mockitokotlin2.any
 import com.nhaarman.mockitokotlin2.inOrder
 import com.nhaarman.mockitokotlin2.spy
 import com.nhaarman.mockitokotlin2.verify
 import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
+import org.junit.After
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Rule
@@ -91,6 +96,16 @@
         assertTrue(setupLatch.await(1000, TimeUnit.SECONDS))
     }
 
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
     @Test
     fun ui_pointerMovementWithinTouchSlop_noCallbacksCalled() {
 
@@ -434,6 +449,21 @@
 
         verify(scaleObserver).onScale(.2f)
     }
+
+    @Test
+    fun testInspectorValue() {
+        val observer = MyScaleObserver()
+        activityTestRule.runOnUiThread {
+            val modifier = Modifier.scaleGestureFilter(observer)
+                as InspectableValue
+
+            assertThat(modifier.nameFallback).isEqualTo("scaleGestureFilter")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("scaleObserver", observer),
+            )
+        }
+    }
 }
 
 @Suppress("RedundantOverride")
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/ScaleSlopExceededGestureFilterComposeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/ScaleSlopExceededGestureFilterComposeTest.kt
new file mode 100644
index 0000000..a2c3669
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/ScaleSlopExceededGestureFilterComposeTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.gesture
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class ScaleSlopExceededGestureFilterComposeTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @Test
+    fun testInspectorValue() {
+        val onScaleSlopExceeded: () -> Unit = {}
+        rule.setContent {
+            val modifier = Modifier.scaleSlopExceededGestureFilter(onScaleSlopExceeded)
+                as InspectableValue
+
+            assertThat(modifier.nameFallback).isEqualTo("scaleSlopExceededGestureFilter")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("onScaleSlopExceeded", onScaleSlopExceeded)
+            )
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/ScrollGestureFilterComposeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/ScrollGestureFilterComposeTest.kt
new file mode 100644
index 0000000..20ae38b
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/ScrollGestureFilterComposeTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.gesture
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+class ScrollGestureFilterComposeTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @Test
+    fun testInspectorValue() {
+        val scrollCallback = object : ScrollCallback {}
+        val canDrag: (Direction) -> Boolean = { true }
+        rule.setContent {
+            val modifier = Modifier.scrollGestureFilter(
+                scrollCallback,
+                Orientation.Vertical,
+                canDrag,
+                true
+            ) as InspectableValue
+
+            assertThat(modifier.nameFallback).isEqualTo("scrollGestureFilter")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("scrollCallback", scrollCallback),
+                ValueElement("orientation", Orientation.Vertical),
+                ValueElement("canDrag", canDrag),
+                ValueElement("startDragImmediately", true),
+            )
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterTest.kt
index 091e54b..03e43b3 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterTest.kt
@@ -25,18 +25,23 @@
 import android.view.MotionEvent.ACTION_UP
 import android.view.MotionEvent.TOOL_TYPE_UNKNOWN
 import androidx.activity.ComponentActivity
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.gesture.PointerCoords
 import androidx.compose.ui.gesture.PointerProperties
 import androidx.compose.ui.layout.AlignmentLine
 import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.milliseconds
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
+import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -65,6 +70,16 @@
         pointerInteropFilter.requestDisallowInterceptTouchEvent = disallowInterceptRequester
     }
 
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
     // Verification of correct MotionEvents being dispatched (when no events are cancel)
 
     @Test
@@ -4329,6 +4344,21 @@
         assertThat(dispatchedMotionEvents).hasSize(1)
     }
 
+    @Test
+    fun testInspectorValue() {
+        val onTouchEvent: (MotionEvent) -> Boolean = { true }
+        rule.setContent {
+            val modifier = Modifier.pointerInteropFilter(disallowInterceptRequester, onTouchEvent)
+                as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("pointerInteropFilter")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("requestDisallowInterceptTouchEvent", disallowInterceptRequester),
+                ValueElement("onTouchEvent", onTouchEvent)
+            )
+        }
+    }
+
     private class MockCoordinates : LayoutCoordinates {
         override val size: IntSize
             get() = IntSize.Zero
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
index 8abc165..5adf0f7 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
@@ -212,16 +212,16 @@
                 val width = placeables.fold(0) { sum, p -> sum + p.width }
                 val height = placeables.fold(0) { sum, p -> sum + p.height }
                 layout(width, height) {
-                    var x = 0f
-                    var y = 0f
+                    var x = 0
+                    var y = 0
                     for (placeable in placeables) {
                         if (absolutePositioning) {
-                            placeable.place(Offset(x, y))
+                            placeable.place(x, y)
                         } else {
-                            placeable.placeRelative(Offset(x, y))
+                            placeable.placeRelative(x, y)
                         }
-                        x += placeable.width.toFloat()
-                        y += placeable.height.toFloat()
+                        x += placeable.width
+                        y += placeable.height
                     }
                 }
             }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
index da1aae6..02a55a7 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
@@ -32,7 +32,7 @@
 import androidx.compose.ui.background
 import androidx.compose.ui.draw.assertColor
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.platform.AndroidOwnerExtraAssertionsRule
 import androidx.compose.ui.platform.DensityAmbient
@@ -552,6 +552,6 @@
     }
 }
 
-fun ImageAsset.assertCenterPixelColor(expectedColor: Color) {
+fun ImageBitmap.assertCenterPixelColor(expectedColor: Color) {
     asAndroidBitmap().assertColor(expectedColor, width / 2, height / 2)
 }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/WithConstraintsTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/WithConstraintsTest.kt
index ffb2010..9a7ab5d 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/WithConstraintsTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/WithConstraintsTest.kt
@@ -35,7 +35,6 @@
 import androidx.compose.ui.assertRect
 import androidx.compose.ui.draw.paint
 import androidx.compose.ui.drawBehind
-import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.rememberVectorPainter
 import androidx.compose.ui.node.Ref
@@ -45,6 +44,7 @@
 import androidx.compose.ui.runOnUiThreadIR
 import androidx.compose.ui.test.TestActivity
 import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.waitAndScreenShot
@@ -700,7 +700,7 @@
                                 minHeight = minHeightConstraint.toIntPx(),
                                 maxHeight = maxHeightConstraint.toIntPx()
                             )
-                        ).place(Offset.Zero)
+                        ).place(IntOffset.Zero)
                     }
                 }
             }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/res/ResourcesTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/res/ResourcesTest.kt
index 2d724d6..b47ba46 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/res/ResourcesTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/res/ResourcesTest.kt
@@ -18,7 +18,7 @@
 
 import android.util.LruCache
 import androidx.compose.runtime.Providers
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.asAndroidBitmap
 import androidx.compose.ui.graphics.imageFromResource
 import androidx.compose.ui.platform.ContextAmbient
@@ -67,7 +67,7 @@
         val failedImage = imageFromResource(resource, R.drawable.failed_image)
 
         var uiThreadWork: (() -> Unit)? = null
-        var res: DeferredResource<ImageAsset>? = null
+        var res: DeferredResource<ImageBitmap>? = null
 
         rule.setContent {
             Providers(ContextAmbient provides context) {
@@ -130,7 +130,7 @@
         val failedImage = imageFromResource(resource, R.drawable.failed_image)
 
         var uiThreadWork: (() -> Unit)? = null
-        var res: DeferredResource<ImageAsset>? = null
+        var res: DeferredResource<ImageBitmap>? = null
 
         rule.setContent {
             Providers(ContextAmbient provides context) {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/EditTextInteropTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/EditTextInteropTest.kt
new file mode 100644
index 0000000..b000a0a
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/EditTextInteropTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.viewinterop
+
+import android.view.KeyEvent
+import android.view.View
+import android.widget.EditText
+import androidx.activity.ComponentActivity
+import androidx.compose.ui.focus.ExperimentalFocus
+import androidx.compose.ui.platform.ViewAmbient
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.text.InternalTextApi
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@OptIn(ExperimentalFocus::class, InternalTextApi::class)
+@RunWith(AndroidJUnit4::class)
+class EditTextInteropTest {
+    @get:Rule
+    val rule = createAndroidComposeRule<ComponentActivity>()
+
+    @Test
+    fun hardwareKeyInEmbeddedView() {
+        // Arrange.
+        lateinit var editText: EditText
+        lateinit var ownerView: View
+        rule.setContent {
+            ownerView = ViewAmbient.current
+            AndroidView({
+                EditText(it).apply { width = 500; editText = this }
+            })
+        }
+        rule.runOnIdle { editText.requestFocus() }
+
+        // Act.
+        val keyConsumed = rule.runOnIdle {
+            ownerView.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_A))
+        }
+
+        // Assert.
+        // TODO(b/171997891): Right now we just assert that we reach here without crashing. Once we
+        //  propagate hardware keys correctly, assert that the EditText receives the entered value.
+        assertThat(keyConsumed).isFalse()
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilter.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilter.kt
index 6e1811f..d637e37 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilter.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilter.kt
@@ -25,6 +25,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.milliseconds
 import androidx.compose.ui.util.fastAny
@@ -59,7 +60,13 @@
 fun Modifier.pointerInteropFilter(
     requestDisallowInterceptTouchEvent: (RequestDisallowInterceptTouchEvent)? = null,
     onTouchEvent: (MotionEvent) -> Boolean
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "pointerInteropFilter"
+        properties["requestDisallowInterceptTouchEvent"] = requestDisallowInterceptTouchEvent
+        properties["onTouchEvent"] = onTouchEvent
+    }
+) {
     val filter = remember { PointerInteropFilter() }
     filter.onTouchEvent = onTouchEvent
     filter.requestDisallowInterceptTouchEvent = requestDisallowInterceptTouchEvent
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.kt
index 0efb821..c525d28 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.kt
@@ -326,9 +326,17 @@
         return keyInputModifier.processKeyInput(keyEvent)
     }
 
-    override fun dispatchKeyEvent(event: AndroidKeyEvent): Boolean {
-        return sendKeyEvent(KeyEventAndroid(event))
-    }
+    override fun dispatchKeyEvent(event: AndroidKeyEvent) =
+        if (isFocused) {
+            // Focus lies within the Compose hierarchy, so we dispatch the key event to the
+            // appropriate place.
+            sendKeyEvent(KeyEventAndroid(event))
+        } else {
+            // This Owner has a focused child view, which is a view interop use case,
+            // so we use the default ViewGroup behavior which will route tke key event to the
+            // focused view.
+            super.dispatchKeyEvent(event)
+        }
 
     override fun pauseModelReadObserveration(block: () -> Unit) =
         snapshotObserver.pauseObservingReads(block)
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/ImageResources.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/ImageResources.kt
index 584b134..1a0fe6b 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/ImageResources.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/ImageResources.kt
@@ -20,7 +20,7 @@
 import androidx.annotation.DrawableRes
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.imageFromResource
 import androidx.compose.ui.platform.ContextAmbient
 import androidx.compose.ui.util.trace
@@ -34,7 +34,7 @@
  * @return the decoded image data associated with the resource
  */
 @Composable
-fun imageResource(@DrawableRes id: Int): ImageAsset {
+fun imageResource(@DrawableRes id: Int): ImageBitmap {
     val context = ContextAmbient.current
     val value = remember { TypedValue() }
     context.resources.getValue(id, value, true)
@@ -59,9 +59,9 @@
 @Composable
 fun loadImageResource(
     id: Int,
-    pendingImage: ImageAsset? = null,
-    failedImage: ImageAsset? = null
-): DeferredResource<ImageAsset> {
+    pendingImage: ImageBitmap? = null,
+    failedImage: ImageBitmap? = null
+): DeferredResource<ImageBitmap> {
     val context = ContextAmbient.current
     val res = context.resources
     val value = remember { TypedValue() }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt
index 28d0a29..a5f2405 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt
@@ -16,7 +16,9 @@
 
 package androidx.compose.ui.text.input
 
+import android.annotation.SuppressLint
 import android.content.Context
+import android.os.Build
 import android.text.InputType
 import android.view.View
 import android.view.ViewTreeObserver
@@ -245,6 +247,14 @@
             }
         }
 
+        outInfo.initialSelStart = state.selection.start
+        outInfo.initialSelEnd = state.selection.end
+
+        @SuppressLint("UnsafeNewApiCall")
+        if (Build.VERSION.SDK_INT >= 30) {
+            outInfo.setInitialSurroundingText(state.text)
+        }
+
         outInfo.imeOptions = outInfo.imeOptions or EditorInfo.IME_FLAG_NO_FULLSCREEN
     }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusObserverModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusObserverModifier.kt
index 7758df1..4546434 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusObserverModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusObserverModifier.kt
@@ -18,6 +18,9 @@
 
 import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.debugInspectorInfo
 
 /**
  * A [modifier][Modifier.Element] that can be used to observe focus state changes.
@@ -32,13 +35,22 @@
 
 @OptIn(ExperimentalFocus::class)
 internal class FocusObserverModifierImpl(
-    override val onFocusChange: (FocusState) -> Unit
-) : FocusObserverModifier
+    override val onFocusChange: (FocusState) -> Unit,
+    inspectorInfo: InspectorInfo.() -> Unit
+) : FocusObserverModifier, InspectorValueInfo(inspectorInfo)
 
 /**
  * Add this modifier to a component to observe focus state changes.
  */
 @ExperimentalFocus
 fun Modifier.focusObserver(onFocusChange: (FocusState) -> Unit): Modifier {
-    return this.then(FocusObserverModifierImpl(onFocusChange))
+    return this.then(
+        FocusObserverModifierImpl(
+            onFocusChange = onFocusChange,
+            inspectorInfo = debugInspectorInfo {
+                name = "focusObserver"
+                properties["onFocusChange"] = onFocusChange
+            }
+        )
+    )
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusRequesterModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusRequesterModifier.kt
index acfbf3c..bd049b0 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusRequesterModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/FocusRequesterModifier.kt
@@ -18,6 +18,9 @@
 
 import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.debugInspectorInfo
 
 /**
  * A [modifier][Modifier.Element] that can be used to pass in a [FocusRequester] that can be used
@@ -35,13 +38,22 @@
 
 @OptIn(ExperimentalFocus::class)
 internal class FocusRequesterModifierImpl(
-    override val focusRequester: FocusRequester
-) : FocusRequesterModifier
+    override val focusRequester: FocusRequester,
+    inspectorInfo: InspectorInfo.() -> Unit
+) : FocusRequesterModifier, InspectorValueInfo(inspectorInfo)
 
 /**
  * Add this modifier to a component to observe changes to focus state.
  */
 @ExperimentalFocus
 fun Modifier.focusRequester(focusRequester: FocusRequester): Modifier {
-    return this.then(FocusRequesterModifierImpl(focusRequester))
+    return this.then(
+        FocusRequesterModifierImpl(
+            focusRequester = focusRequester,
+            inspectorInfo = debugInspectorInfo {
+                name = "focusRequester"
+                properties["focusRequester"] = focusRequester
+            }
+        )
+    )
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ZIndexModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ZIndexModifier.kt
index 92fe7b5..80850d8 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ZIndexModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ZIndexModifier.kt
@@ -17,34 +17,20 @@
 package androidx.compose.ui
 
 import androidx.compose.runtime.Stable
+import androidx.compose.ui.layout.LayoutModifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.platform.InspectorValueInfo
 import androidx.compose.ui.platform.debugInspectorInfo
-
-/**
- * A [Modifier.Element] that controls the drawing order for the children of the same layout
- * parent. A child with larger [zIndex] will be drawn on top of all the children with smaller
- * [zIndex]. When children have the same [zIndex] the original order in which the items were
- * added into the parent layout is applied.
- *
- * Note that if there would be multiple [ZIndexModifier] modifiers applied for the same layout
- * the sum of their values will be used as the final zIndex. If no [ZIndexModifier]s applied for the
- * layout then zIndex for this Layout is 0.
- *
- * @see [Modifier.zIndex]
- */
-// TODO("Made it internal to be able to later migrate Modifier.zIndex() implementation without
-//  the breaking change to work as LayoutModifier where we provide zIndex right as a param for
-//  placeable.place() call. Tracked in b/171493718")
-internal interface ZIndexModifier : Modifier.Element {
-    val zIndex: Float
-}
+import androidx.compose.ui.unit.Constraints
 
 /**
  * Creates a modifier that controls the drawing order for the children of the same layout parent.
  * A child with larger [zIndex] will be drawn on top of all the children with smaller [zIndex].
- * When children have the same [zIndex] the original order in which the items were added into the
- * parent layout is applied.
+ * When children have the same [zIndex] the original order in which the parent placed the
+ * children is used.
  *
  * Note that if there would be multiple [zIndex] modifiers applied for the same layout
  * the sum of their values will be used as the final zIndex. If no [zIndex] were applied for the
@@ -54,7 +40,7 @@
  */
 @Stable
 fun Modifier.zIndex(zIndex: Float): Modifier = this.then(
-    SimpleZIndexModifier(
+    ZIndexModifier(
         zIndex = zIndex,
         inspectorInfo = debugInspectorInfo {
             name = "zIndex"
@@ -63,19 +49,27 @@
     )
 )
 
-private class SimpleZIndexModifier(
-    override val zIndex: Float,
+private class ZIndexModifier(
+    private val zIndex: Float,
     inspectorInfo: InspectorInfo.() -> Unit
-) : ZIndexModifier, InspectorValueInfo(inspectorInfo) {
+) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
 
-    override fun hashCode(): Int =
-        zIndex.hashCode()
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        val placeable = measurable.measure(constraints)
+        return layout(placeable.width, placeable.height) {
+            placeable.place(0, 0, zIndex = zIndex)
+        }
+    }
+
+    override fun hashCode(): Int = zIndex.hashCode()
 
     override fun equals(other: Any?): Boolean {
-        val otherModifier = other as? SimpleZIndexModifier ?: return false
+        val otherModifier = other as? ZIndexModifier ?: return false
         return zIndex == otherModifier.zIndex
     }
 
-    override fun toString(): String =
-        "SimpleZIndexModifier(zIndex=$zIndex)"
+    override fun toString(): String = "ZIndexModifier(zIndex=$zIndex)"
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawShadow.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawShadow.kt
index 231f86f..0cfff37 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawShadow.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawShadow.kt
@@ -23,6 +23,7 @@
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.platform.DensityAmbient
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 
@@ -48,7 +49,14 @@
     shape: Shape = RectangleShape,
     clip: Boolean = elevation > 0.dp
 ) = if (elevation > 0.dp || clip) {
-    composed {
+    composed(
+        inspectorInfo = debugInspectorInfo {
+            name = "drawShadow"
+            properties["elevation"] = elevation
+            properties["shape"] = shape
+            properties["clip"] = clip
+        }
+    ) {
         drawLayer(
             shadowElevation = with(DensityAmbient.current) { elevation.toPx() },
             shape = shape,
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilter.kt
index c725a4e..dd36b98 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilter.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerInputFilter
 import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.IntSize
 
 interface LongPressDragObserver {
@@ -118,7 +119,12 @@
  */
 fun Modifier.longPressDragGestureFilter(
     longPressDragObserver: LongPressDragObserver
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "longPressDragGestureFilter"
+        properties["longPressDragObserver"] = longPressDragObserver
+    }
+) {
     val glue = remember { LongPressDragGestureDetectorGlue() }
     glue.longPressDragObserver = longPressDragObserver
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/LongPressGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/LongPressGestureFilter.kt
index 0f655f6..7619155 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/LongPressGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/LongPressGestureFilter.kt
@@ -33,6 +33,7 @@
 import androidx.compose.ui.input.pointer.changedToUp
 import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
 import androidx.compose.ui.input.pointer.consumeDownChange
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.inMilliseconds
 import androidx.compose.ui.util.annotation.VisibleForTesting
@@ -55,7 +56,12 @@
  */
 fun Modifier.longPressGestureFilter(
     onLongPress: (Offset) -> Unit
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "longPressGestureFilter"
+        properties["onLongPress"] = onLongPress
+    }
+) {
     @Suppress("DEPRECATION")
     val scope = rememberCoroutineScope()
     val filter = remember { LongPressGestureFilter(scope) }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/PressIndicatorGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/PressIndicatorGestureFilter.kt
index 077c908..ad7dc32 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/PressIndicatorGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/PressIndicatorGestureFilter.kt
@@ -28,6 +28,7 @@
 import androidx.compose.ui.input.pointer.changedToDown
 import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
 import androidx.compose.ui.input.pointer.consumeDownChange
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.fastAny
 import androidx.compose.ui.util.fastForEach
@@ -54,7 +55,15 @@
     onStop: (() -> Unit)? = null,
     onCancel: (() -> Unit)? = null,
     enabled: Boolean = true
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "pressIndicatorGestureFilter"
+        properties["onStart"] = onStart
+        properties["onStop"] = onStop
+        properties["onCancel"] = onCancel
+        properties["enabled"] = enabled
+    }
+) {
     val filter = remember { PressIndicatorGestureFilter() }
     filter.onStart = onStart
     filter.onStop = onStop
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawDragGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawDragGestureFilter.kt
index 784548b..250512b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawDragGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawDragGestureFilter.kt
@@ -38,6 +38,7 @@
 import androidx.compose.ui.input.pointer.consumeDownChange
 import androidx.compose.ui.input.pointer.consumePositionChange
 import androidx.compose.ui.input.pointer.positionChange
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.fastAny
 import androidx.compose.ui.util.fastForEach
@@ -150,7 +151,14 @@
     dragObserver: DragObserver,
     canStartDragging: (() -> Boolean)? = null,
     orientation: Orientation? = null
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "rawDragGestureFilter"
+        properties["dragObserver"] = dragObserver
+        properties["canStartDragging"] = canStartDragging
+        properties["orientation"] = orientation
+    }
+) {
     val filter = remember { RawDragGestureFilter() }
     filter.dragObserver = dragObserver
     filter.canStartDragging = canStartDragging
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawPressStartGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawPressStartGestureFilter.kt
index 543e36a..7efd427 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawPressStartGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawPressStartGestureFilter.kt
@@ -26,6 +26,7 @@
 import androidx.compose.ui.input.pointer.changedToDown
 import androidx.compose.ui.input.pointer.changedToUp
 import androidx.compose.ui.input.pointer.consumeDownChange
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.fastForEach
 
@@ -54,7 +55,14 @@
     onPressStart: (Offset) -> Unit,
     enabled: Boolean = false,
     executionPass: PointerEventPass = PointerEventPass.Main
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "rawPressStartGestureFilter"
+        properties["onPressStart"] = onPressStart
+        properties["enabled"] = enabled
+        properties["executionPass"] = executionPass
+    }
+) {
     val filter = remember { RawPressStartGestureFilter() }
     filter.onPressStart = onPressStart
     filter.setEnabled(enabled = enabled)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawScaleGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawScaleGestureFilter.kt
index abf751b..b4c1257 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawScaleGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/RawScaleGestureFilter.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.input.pointer.PointerInputFilter
 import androidx.compose.ui.input.pointer.consumeDownChange
 import androidx.compose.ui.input.pointer.consumePositionChange
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.fastForEach
 
@@ -119,7 +120,13 @@
 fun Modifier.rawScaleGestureFilter(
     scaleObserver: RawScaleObserver,
     canStartScaling: (() -> Boolean)? = null
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "rawScaleGestureFilter"
+        properties["scaleObserver"] = scaleObserver
+        properties["canStartScaling"] = canStartScaling
+    }
+) {
     val filter = remember { RawScaleGestureFilter() }
     // TODO(b/129784010): Consider also allowing onStart, onScale, and onEnd to be set individually.
     filter.scaleObserver = scaleObserver
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScaleGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScaleGestureFilter.kt
index 0e44d48..3c91cde 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScaleGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScaleGestureFilter.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
+import androidx.compose.ui.platform.debugInspectorInfo
 
 /**
  * Observes various events sent by [scaleGestureFilter].  Implement and pass into
@@ -103,7 +104,12 @@
  */
 fun Modifier.scaleGestureFilter(
     scaleObserver: ScaleObserver
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "scaleGestureFilter"
+        properties["scaleObserver"] = scaleObserver
+    }
+) {
     val glue = remember { TouchSlopScaleGestureDetectorGlue() }
     glue.scaleObserver = scaleObserver
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScaleSlopExceededGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScaleSlopExceededGestureFilter.kt
index e7e1820..e3243d4 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScaleSlopExceededGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScaleSlopExceededGestureFilter.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.input.pointer.PointerInputFilter
 import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
 import androidx.compose.ui.platform.DensityAmbient
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.IntSize
 import kotlin.math.absoluteValue
 
@@ -48,7 +49,12 @@
  */
 fun Modifier.scaleSlopExceededGestureFilter(
     onScaleSlopExceeded: () -> Unit
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "scaleSlopExceededGestureFilter"
+        properties["onScaleSlopExceeded"] = onScaleSlopExceeded
+    }
+) {
     val scaleSlop = with(DensityAmbient.current) { ScaleSlop.toPx() }
     val filter = remember { ScaleSlopExceededGestureFilter(scaleSlop) }
     // TODO(b/129784010): Consider also allowing onStart, onScale, and onEnd to be set individually.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScrollGestureFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScrollGestureFilter.kt
index 0c0e60b..0e569c1 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScrollGestureFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/gesture/ScrollGestureFilter.kt
@@ -23,6 +23,7 @@
 import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.gesture.scrollorientationlocking.ScrollOrientationLocker
 import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.platform.debugInspectorInfo
 
 /**
  * Defines the callbacks associated with scrolling.
@@ -117,7 +118,15 @@
     orientation: Orientation,
     canDrag: ((Direction) -> Boolean)? = null,
     startDragImmediately: Boolean = false
-): Modifier = composed {
+): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "scrollGestureFilter"
+        properties["scrollCallback"] = scrollCallback
+        properties["orientation"] = orientation
+        properties["canDrag"] = canDrag
+        properties["startDragImmediately"] = startDragImmediately
+    }
+) {
     val coordinator = remember { ScrollGestureFilterCoordinator() }
     coordinator.scrollCallback = scrollCallback
     coordinator.orientation = orientation
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/DrawCache.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/DrawCache.kt
index 397678e..99a0886 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/DrawCache.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/DrawCache.kt
@@ -21,7 +21,7 @@
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.unit.Density
@@ -30,7 +30,7 @@
 import androidx.compose.ui.unit.toSize
 
 /**
- * Creates a drawing environment that directs its drawing commands to an [ImageAsset]
+ * Creates a drawing environment that directs its drawing commands to an [ImageBitmap]
  * which can be drawn directly in another [DrawScope] instance. This is useful to cache
  * complicated drawing commands across frames especially if the content has not changed.
  * Additionally some drawing operations such as rendering paths are done purely in
@@ -39,7 +39,7 @@
  */
 internal class DrawCache {
 
-    @PublishedApi internal var cachedImage: ImageAsset? = null
+    @PublishedApi internal var mCachedImage: ImageBitmap? = null
     private var cachedCanvas: Canvas? = null
     private var scopeDensity: Density? = null
     private var layoutDirection: LayoutDirection = LayoutDirection.Ltr
@@ -47,8 +47,8 @@
     private val cacheScope = CanvasDrawScope()
 
     /**
-     * Draw the contents of the lambda with receiver scope into an [ImageAsset] with the provided
-     * size. If the same size is provided across calls, the same [ImageAsset] instance is
+     * Draw the contents of the lambda with receiver scope into an [ImageBitmap] with the provided
+     * size. If the same size is provided across calls, the same [ImageBitmap] instance is
      * re-used and the contents are cleared out before drawing content in it again
      */
     fun drawCachedImage(
@@ -59,17 +59,17 @@
     ) {
         this.scopeDensity = density
         this.layoutDirection = layoutDirection
-        var targetImage = cachedImage
+        var targetImage = mCachedImage
         var targetCanvas = cachedCanvas
         if (targetImage == null ||
             targetCanvas == null ||
             size.width > targetImage.width ||
             size.height > targetImage.height
         ) {
-            targetImage = ImageAsset(size.width, size.height)
+            targetImage = ImageBitmap(size.width, size.height)
             targetCanvas = Canvas(targetImage)
 
-            cachedImage = targetImage
+            mCachedImage = targetImage
             cachedCanvas = targetCanvas
         }
         cacheScope.draw(density, layoutDirection, targetCanvas, size.toSize()) {
@@ -87,7 +87,7 @@
         alpha: Float = 1.0f,
         colorFilter: ColorFilter? = null
     ) {
-        val targetImage = cachedImage
+        val targetImage = mCachedImage
         check(targetImage != null) {
             "drawCachedImage must be invoked first before attempting to draw the result " +
                 "into another destination"
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
index 40050aa..dc18697 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
@@ -19,6 +19,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.node.ModifiedKeyInputNode
+import androidx.compose.ui.platform.debugInspectorInfo
 
 /**
  * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
@@ -29,7 +30,12 @@
  * false, the key event will be sent to this [keyInputFilter]'s parent.
  */
 @ExperimentalKeyInput
-fun Modifier.keyInputFilter(onKeyEvent: (KeyEvent) -> Boolean): Modifier = composed {
+fun Modifier.keyInputFilter(onKeyEvent: (KeyEvent) -> Boolean): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "keyInputFilter"
+        properties["onKeyEvent"] = onKeyEvent
+    }
+) {
     KeyInputModifier(onKeyEvent = onKeyEvent, onPreviewKeyEvent = null)
 }
 
@@ -44,7 +50,12 @@
  * sent back up to the root [keyInputFilter] using the onKeyEvent callback.
  */
 @ExperimentalKeyInput
-fun Modifier.previewKeyInputFilter(onPreviewKeyEvent: (KeyEvent) -> Boolean): Modifier = composed {
+fun Modifier.previewKeyInputFilter(onPreviewKeyEvent: (KeyEvent) -> Boolean): Modifier = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "previewKeyInputFilter"
+        properties["onPreviewKeyEvent"] = onPreviewKeyEvent
+    }
+) {
     KeyInputModifier(onKeyEvent = null, onPreviewKeyEvent = onPreviewKeyEvent)
 }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
index 1b64b0c..3401337 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
@@ -290,7 +290,7 @@
         measuredSize = IntSize(width, height)
     }
     override fun get(line: AlignmentLine): Int = AlignmentLine.Unspecified
-    override fun placeAt(position: IntOffset) { }
+    override fun placeAt(position: IntOffset, zIndex: Float) { }
 }
 
 /**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutModifier.kt
index 77d7ff5..e2146fe 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutModifier.kt
@@ -228,7 +228,7 @@
             measuredSize = IntSize(width, height)
         }
         override fun get(line: AlignmentLine): Int = AlignmentLine.Unspecified
-        override fun placeAt(position: IntOffset) { }
+        override fun placeAt(position: IntOffset, zIndex: Float) { }
     }
 
     private enum class IntrinsicMinMax { Min, Max }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
index 28297cd..62ecf52 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
@@ -76,8 +76,12 @@
 
     /**
      * Positions the [Placeable] at [position] in its parent's coordinate system.
+     *
+     * @param zIndex controls the drawing order for the [Placeable]. A [Placeable] with larger
+     * [zIndex] will be drawn on top of all the children with smaller [zIndex]. When children
+     * have the same [zIndex] the order in which the items were placed is used.
      */
-    protected abstract fun placeAt(position: IntOffset)
+    protected abstract fun placeAt(position: IntOffset, zIndex: Float)
 
     /**
      * The constraints used for the measurement made to obtain this [Placeable].
@@ -128,8 +132,13 @@
          * If this method is used outside the [MeasureScope.layout] positioning block, the
          * automatic position mirroring will not happen and the [Placeable] will be placed at the
          * given [position], similar to the [place] method.
+         *
+         * @param zIndex controls the drawing order for the [Placeable]. A [Placeable] with larger
+         * [zIndex] will be drawn on top of all the children with smaller [zIndex]. When children
+         * have the same [zIndex] the order in which the items were placed is used.
          */
-        fun Placeable.placeRelative(position: IntOffset) = placeAutoMirrored(position)
+        fun Placeable.placeRelative(position: IntOffset, zIndex: Float = 0f) =
+            placeAutoMirrored(position, zIndex)
 
         /**
          * Place a [Placeable] at [position] in its parent's coordinate system.
@@ -140,7 +149,11 @@
          * automatic position mirroring will not happen and the [Placeable] will be placed at the
          * given [position], similar to the [place] method.
          */
-        fun Placeable.placeRelative(position: Offset) = placeAutoMirrored(position.round())
+        @Deprecated(
+            "Use the overloads with IntOffset instead",
+            ReplaceWith("placeRelative(position.round())", "androidx.compose.ui.unit.round")
+        )
+        fun Placeable.placeRelative(position: Offset) = placeRelative(position.round())
 
         /**
          * Place a [Placeable] at [x], [y] in its parent's coordinate system.
@@ -150,36 +163,53 @@
          * If this method is used outside the [MeasureScope.layout] positioning block, the
          * automatic position mirroring will not happen and the [Placeable] will be placed at the
          * given position, similar to the [place] method.
+         *
+         * @param zIndex controls the drawing order for the [Placeable]. A [Placeable] with larger
+         * [zIndex] will be drawn on top of all the children with smaller [zIndex]. When children
+         * have the same [zIndex] the order in which the items were placed is used.
          */
-        fun Placeable.placeRelative(x: Int, y: Int) = placeAutoMirrored(IntOffset(x, y))
+        fun Placeable.placeRelative(x: Int, y: Int, zIndex: Float = 0f) =
+            placeAutoMirrored(IntOffset(x, y), zIndex)
 
         /**
          * Place a [Placeable] at [position] in its parent's coordinate system.
          * Unlike [placeRelative], the given [position] will not implicitly react in RTL layout direction
          * contexts.
          */
+        @Deprecated(
+            "Use the overloads with IntOffset instead",
+            ReplaceWith("place(position.round())", "androidx.compose.ui.unit.round")
+        )
         fun Placeable.place(position: Offset) = place(position.round())
 
         /**
          * Place a [Placeable] at [x], [y] in its parent's coordinate system.
          * Unlike [placeRelative], the given position will not implicitly react in RTL layout direction
          * contexts.
+         *
+         * @param zIndex controls the drawing order for the [Placeable]. A [Placeable] with larger
+         * [zIndex] will be drawn on top of all the children with smaller [zIndex]. When children
+         * have the same [zIndex] the order in which the items were placed is used.
          */
-        fun Placeable.place(x: Int, y: Int) = place(IntOffset(x, y))
+        fun Placeable.place(x: Int, y: Int, zIndex: Float = 0f) = placeAt(IntOffset(x, y), zIndex)
 
         /**
          * Place a [Placeable] at [position] in its parent's coordinate system.
          * Unlike [placeRelative], the given [position] will not implicitly react in RTL layout direction
          * contexts.
+         *
+         * @param zIndex controls the drawing order for the [Placeable]. A [Placeable] with larger
+         * [zIndex] will be drawn on top of all the children with smaller [zIndex]. When children
+         * have the same [zIndex] the order in which the items were placed is used.
          */
-        fun Placeable.place(position: IntOffset) =
-            placeAt(position + apparentToRealOffset)
+        fun Placeable.place(position: IntOffset, zIndex: Float = 0f) =
+            placeAt(position + apparentToRealOffset, zIndex)
 
-        private fun Placeable.placeAutoMirrored(position: IntOffset) {
+        private fun Placeable.placeAutoMirrored(position: IntOffset, zIndex: Float) {
             if (parentLayoutDirection == LayoutDirection.Ltr || parentWidth == 0) {
-                place(position)
+                place(position, zIndex)
             } else {
-                place(IntOffset(parentWidth - measuredSize.width - position.x, position.y))
+                place(IntOffset(parentWidth - measuredSize.width - position.x, position.y), zIndex)
             }
         }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
index dc8701d..a60d0c2 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
@@ -83,8 +83,9 @@
 
     override fun get(line: AlignmentLine): Int = wrapped[line]
 
-    override fun placeAt(position: IntOffset) {
+    override fun placeAt(position: IntOffset, zIndex: Float) {
         this.position = position
+        this.zIndex = zIndex
 
         // The wrapper only runs their placement block to obtain our position, which allows them
         // to calculate the offset of an alignment line we have already provided a position for.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
index 19e75a9..57f0705 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
@@ -105,8 +105,9 @@
         )
     }
 
-    override fun placeAt(position: IntOffset) {
+    override fun placeAt(position: IntOffset, zIndex: Float) {
         this.position = position
+        this.zIndex = zIndex
 
         // The wrapper only runs their placement block to obtain our position, which allows them
         // to calculate the offset of an alignment line we have already provided a position for.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayerWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayerWrapper.kt
index 90fde06..0833a6a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayerWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayerWrapper.kt
@@ -69,8 +69,8 @@
         return placeable
     }
 
-    override fun placeAt(position: IntOffset) {
-        super.placeAt(position)
+    override fun placeAt(position: IntOffset, zIndex: Float) {
+        super.placeAt(position, zIndex)
         layer.move(position)
     }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
index 421abd5..1b2604e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
@@ -31,7 +31,6 @@
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.Remeasurement
 import androidx.compose.ui.layout.RemeasurementModifier
-import androidx.compose.ui.ZIndexModifier
 import androidx.compose.ui.focus.ExperimentalFocus
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
@@ -207,6 +206,7 @@
 
         instance.parent = this
         _foldedChildren.add(index, instance)
+        zSortedChildrenInvalidated = true
 
         if (instance.isVirtual) {
             require(!isVirtual) { "Virtual LayoutNode can't be added into a virtual parent" }
@@ -232,6 +232,7 @@
         val attached = owner != null
         for (i in index + count - 1 downTo index) {
             val child = _foldedChildren.removeAt(i)
+            zSortedChildrenInvalidated = true
             if (DebugChanges) {
                 println("$child removed from $this at index $i")
             }
@@ -261,6 +262,7 @@
             child.parent = null
         }
         _foldedChildren.clear()
+        zSortedChildrenInvalidated = true
 
         virtualChildrenCount = 0
         invalidateUnfoldedVirtualChildren()
@@ -290,6 +292,7 @@
 
             _foldedChildren.add(toIndex, child)
         }
+        zSortedChildrenInvalidated = true
 
         invalidateUnfoldedVirtualChildren()
         requestRemeasure()
@@ -363,6 +366,7 @@
     }
 
     private val _zSortedChildren = mutableVectorOf<LayoutNode>()
+    private var zSortedChildrenInvalidated = true
 
     /**
      * Returns the children list sorted by their [LayoutNode.zIndex] first (smaller first) and the
@@ -375,9 +379,11 @@
     @PublishedApi
     internal val zSortedChildren: MutableVector<LayoutNode>
         get() {
-            _zSortedChildren.clear()
-            _zSortedChildren.addAll(_children)
-            _zSortedChildren.sortWith(ZComparator)
+            if (zSortedChildrenInvalidated) {
+                _zSortedChildren.clear()
+                _zSortedChildren.addAll(_children)
+                _zSortedChildren.sortWith(ZComparator)
+            }
             return _zSortedChildren
         }
 
@@ -616,22 +622,11 @@
 
     /**
      * zIndex defines the drawing order of the LayoutNode. Children with larger zIndex are drawn
-     * after others (the original order is used for the nodes with the same zIndex).
-     * Default zIndex is 0. We use sum of the values of all [ZIndexModifier] as a zIndex.
+     * on top of others (the original order is used for the nodes with the same zIndex).
+     * Default zIndex is 0. We use sum of the values passed as zIndex to place() by the
+     * parent layout and all the applied modifiers.
      */
-    private val zIndex: Float
-        get() = if (zIndexModifiers.isEmpty()) {
-            0f
-        } else {
-            zIndexModifiers.fold(0f) { acc, item ->
-                acc + item.zIndex
-            }
-        }
-
-    /**
-     * All [ZIndexModifier]s added to the node.
-     */
-    private val zIndexModifiers = mutableVectorOf<ZIndexModifier>()
+    private var zIndex: Float = 0f
 
     /**
      * The inner-most layer wrapper. Used for performance for LayoutNodeWrapper.findLayer().
@@ -665,7 +660,6 @@
             field = value
 
             val invalidateParentLayer = shouldInvalidateParentLayer()
-            val startZIndex = zIndex
 
             copyWrappersToCache()
 
@@ -677,7 +671,6 @@
             val addedCallback = hasNewPositioningCallback()
             onPositionedCallbacks.clear()
             onRemeasuredCallbacks.clear()
-            zIndexModifiers.clear()
             innerLayerWrapper = null
 
             // Create a new chain of LayoutNodeWrappers, reusing existing ones from wrappers
@@ -690,9 +683,6 @@
                 if (mod is OnRemeasuredModifier) {
                     onRemeasuredCallbacks += mod
                 }
-                if (mod is ZIndexModifier) {
-                    zIndexModifiers += mod
-                }
                 if (mod is RemeasurementModifier) {
                     mod.onRemeasurementAvailable(this)
                 }
@@ -781,9 +771,7 @@
             if (oldParentData != parentData) {
                 parent?.requestRemeasure()
             }
-            if (invalidateParentLayer || startZIndex != zIndex ||
-                shouldInvalidateParentLayer()
-            ) {
+            if (invalidateParentLayer || shouldInvalidateParentLayer()) {
                 parent?.invalidateLayer()
             }
         }
@@ -896,6 +884,16 @@
     internal fun onNodePlaced() {
         val parent = parent
 
+        var newZIndex = outerMeasurablePlaceable.lastZIndex + innerLayoutNodeWrapper.zIndex
+        forEachDelegate {
+            newZIndex += it.zIndex
+        }
+        if (newZIndex != zIndex) {
+            zIndex = newZIndex
+            zSortedChildrenInvalidated = true
+            parent?.invalidateLayer()
+        }
+
         if (!isPlaced) {
             isPlaced = true
             // when the visibility of a child has been changed we need to invalidate
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
index fe8c3f8..b71473c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
@@ -82,6 +82,9 @@
             field = value
         }
 
+    var zIndex: Float = 0f
+        protected set
+
     override val parentCoordinates: LayoutCoordinates?
         get() {
             check(isAttached) { ExpectAttachedLayoutCoordinates }
@@ -127,7 +130,7 @@
     /**
      * Places the modified child.
      */
-    abstract override fun placeAt(position: IntOffset)
+    abstract override fun placeAt(position: IntOffset, zIndex: Float)
 
     /**
      * Draws the content of the LayoutNode
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt
index 3b30a8f..dab373c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedLayoutNode.kt
@@ -67,7 +67,7 @@
         }
         // Place our wrapped to obtain their position inside ourselves.
         isShallowPlacing = true
-        placeAt(this.position)
+        placeAt(this.position, zIndex = 0f)
         isShallowPlacing = false
         return if (line is HorizontalAlignmentLine) {
             positionInWrapped + wrapped.position.y
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OuterMeasurablePlaceable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OuterMeasurablePlaceable.kt
index d806e34..79f7564 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OuterMeasurablePlaceable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OuterMeasurablePlaceable.kt
@@ -46,6 +46,9 @@
     override var parentData: Any? = null
         private set
 
+    var lastZIndex: Float = 0f
+        private set
+
     /**
      * The function to be executed when the parent layout measures its children.
      */
@@ -109,8 +112,9 @@
 
     override fun get(line: AlignmentLine): Int = outerWrapper[line]
 
-    override fun placeAt(position: IntOffset) {
+    override fun placeAt(position: IntOffset, zIndex: Float) {
         lastPosition = position
+        lastZIndex = zIndex
         with(PlacementScope) {
             outerWrapper.place(position)
         }
@@ -120,7 +124,7 @@
      * Calls [placeAt] with the same position used during the last [placeAt] call
      */
     fun replace() {
-        placeAt(checkNotNull(lastPosition))
+        placeAt(checkNotNull(lastPosition), lastZIndex)
     }
 
     override fun minIntrinsicWidth(height: Int): Int = outerWrapper.minIntrinsicWidth(height)
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/res/ImageResources.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/res/ImageResources.kt
index 2fe8874..7cc8516 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/res/ImageResources.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/res/ImageResources.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
-import androidx.compose.ui.graphics.ImageAsset
+import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.imageFromResource
 
 /**
@@ -30,6 +30,6 @@
  * @return the decoded image data associated with the resource
  */
 @Composable
-fun imageResource(path: String): ImageAsset {
+fun imageResource(path: String): ImageBitmap {
     return remember(path) { imageFromResource(path) }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusObserverModifierTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusObserverModifierTest.kt
new file mode 100644
index 0000000..682eed0
--- /dev/null
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusObserverModifierTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui
+
+import androidx.compose.ui.focus.ExperimentalFocus
+import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+class FocusObserverModifierTest {
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @OptIn(ExperimentalFocus::class)
+    @Test
+    fun testInspectorValue() {
+        val onFocusChange: (FocusState) -> Unit = {}
+        val modifier = Modifier.focusObserver(onFocusChange) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("focusObserver")
+        assertThat(modifier.valueOverride).isNull()
+        assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+            ValueElement("onFocusChange", onFocusChange)
+        )
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusRequesterModifierTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusRequesterModifierTest.kt
new file mode 100644
index 0000000..d54d1d1
--- /dev/null
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/FocusRequesterModifierTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui
+
+import androidx.compose.ui.focus.ExperimentalFocus
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+class FocusRequesterModifierTest {
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @OptIn(ExperimentalFocus::class)
+    @Test
+    fun testInspectorValue() {
+        val focusRequester = FocusRequester()
+        val modifier = Modifier.focusRequester(focusRequester) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("focusRequester")
+        assertThat(modifier.valueOverride).isNull()
+        assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+            ValueElement("focusRequester", focusRequester)
+        )
+    }
+}
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidTest.kt
index 7dd46f2..3d7118f 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidTest.kt
@@ -22,17 +22,19 @@
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputMethodManager
 import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.KeyboardCapitalization
 import androidx.compose.ui.text.input.ImeOptions
+import androidx.compose.ui.text.input.KeyboardCapitalization
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.TextInputServiceAndroid
 import com.nhaarman.mockitokotlin2.eq
 import com.nhaarman.mockitokotlin2.mock
 import com.nhaarman.mockitokotlin2.whenever
-import org.junit.Assert.assertTrue
+import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -638,4 +640,37 @@
             assertTrue((InputType.TYPE_TEXT_FLAG_AUTO_CORRECT and info.inputType) == 0)
         }
     }
+
+    @Test
+    fun initial_default_selection_info_is_set() {
+        textInputService.startInput(
+            value = TextFieldValue(),
+            imeOptions = ImeOptions.Default,
+            onEditCommand = {},
+            onImeActionPerformed = {}
+        )
+
+        EditorInfo().let { info ->
+            textInputService.createInputConnection(info)
+            assertEquals(info.initialSelStart, 0)
+            assertEquals(info.initialSelEnd, 0)
+        }
+    }
+
+    @Test
+    fun initial_selection_info_is_set() {
+        val selection = TextRange(1, 2)
+        textInputService.startInput(
+            value = TextFieldValue("abc", selection),
+            imeOptions = ImeOptions.Default,
+            onEditCommand = {},
+            onImeActionPerformed = {}
+        )
+
+        EditorInfo().let { info ->
+            textInputService.createInputConnection(info)
+            assertEquals(info.initialSelStart, selection.start)
+            assertEquals(info.initialSelEnd, selection.end)
+        }
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/key/KeyInputModifierTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/key/KeyInputModifierTest.kt
new file mode 100644
index 0000000..d072a85
--- /dev/null
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/key/KeyInputModifierTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.input.key
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+class KeyInputModifierTest {
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @OptIn(ExperimentalKeyInput::class)
+    @Test
+    fun testInspectorValueForKeyInputFilter() {
+        val onKeyEvent: (KeyEvent) -> Boolean = { true }
+        val modifier = Modifier.keyInputFilter(onKeyEvent) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("keyInputFilter")
+        assertThat(modifier.valueOverride).isNull()
+        assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+            ValueElement("onKeyEvent", onKeyEvent)
+        )
+    }
+
+    @OptIn(ExperimentalKeyInput::class)
+    @Test
+    fun testInspectorValueForPreviewKeyInputFilter() {
+        val onPreviewKeyEvent: (KeyEvent) -> Boolean = { true }
+        val modifier = Modifier.previewKeyInputFilter(onPreviewKeyEvent) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("previewKeyInputFilter")
+        assertThat(modifier.valueOverride).isNull()
+        assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+            ValueElement("onPreviewKeyEvent", onPreviewKeyEvent)
+        )
+    }
+}
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index ec5f1c7..6b57e64 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -18,8 +18,6 @@
 import androidx.compose.ui.ContentDrawScope
 import androidx.compose.ui.DrawLayerModifier
 import androidx.compose.ui.DrawModifier
-import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.autofill.Autofill
 import androidx.compose.ui.autofill.AutofillTree
@@ -35,7 +33,9 @@
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.input.pointer.PointerInputFilter
 import androidx.compose.ui.input.pointer.PointerInputModifier
+import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.platform.ClipboardManager
 import androidx.compose.ui.platform.TextToolbar
@@ -65,7 +65,6 @@
 import org.junit.rules.ExpectedException
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.Mockito.times
 
 @RunWith(JUnit4::class)
 @OptIn(ExperimentalLayoutNodeApi::class)
@@ -1576,7 +1575,11 @@
         val parent = LayoutNode(
             0, 0, 2, 2
         ).apply {
-            attach(MockOwner())
+            attach(
+                MockOwner().apply {
+                    measureIteration = 1L
+                }
+            )
         }
         parent.insertAt(
             0,
@@ -1596,6 +1599,8 @@
                 )
             )
         )
+        parent.remeasure()
+        parent.replace()
 
         val hit = mutableListOf<PointerInputFilter>()
 
@@ -1703,9 +1708,11 @@
 
     override fun onRequestMeasure(layoutNode: LayoutNode) {
         onRequestMeasureParams += layoutNode
+        layoutNode.layoutState = LayoutNode.LayoutState.NeedsRemeasure
     }
 
     override fun onRequestRelayout(layoutNode: LayoutNode) {
+        layoutNode.layoutState = LayoutNode.LayoutState.NeedsRelayout
     }
 
     override val hasPendingMeasureOrLayout = false
@@ -1794,7 +1801,7 @@
     override fun onSemanticsChange() {
     }
 
-    override val measureIteration: Long = 0
+    override var measureIteration: Long = 0
     override val viewConfiguration: ViewConfiguration
         get() = TODO("Not yet implemented")
 }
@@ -1809,7 +1816,9 @@
                 measurables: List<Measurable>,
                 constraints: Constraints
             ): MeasureResult =
-                measureScope.layout(x2 - x, y2 - y) {}
+                measureScope.layout(x2 - x, y2 - y) {
+                    measurables.forEach { it.measure(constraints).place(0, 0) }
+                }
         }
         attach(MockOwner())
         layoutState = LayoutNode.LayoutState.NeedsRemeasure
diff --git a/core/core-appdigest/build.gradle b/core/core-appdigest/build.gradle
index 1625b7e..8270040 100644
--- a/core/core-appdigest/build.gradle
+++ b/core/core-appdigest/build.gradle
@@ -32,8 +32,8 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
-    implementation project(path: ':core:core')
+    api("androidx.annotation:annotation:1.0.0")
+    api("androidx.core:core:1.0.0")
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index c8aa644..90dc4a5 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -202,8 +202,6 @@
     stubs(fileTree(dir: '../wear/wear_stubs/', include: ['com.google.android.wearable-stubs.jar']))
     docs("androidx.wear:wear-complications-data:1.0.0-alpha02")
     docs("androidx.wear:wear-complications-provider:1.0.0-alpha02")
-    docs("androidx.wear:wear-tiles:1.0.0-alpha01")
-    docs("androidx.wear:wear-tiles-data:1.0.0-alpha01")
     docs("androidx.wear:wear-watchface:1.0.0-alpha02")
     docs("androidx.wear:wear-watchface-client:1.0.0-alpha02")
     docs("androidx.wear:wear-watchface-complications-rendering:1.0.0-alpha02")
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index b26415e..ec1c352 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -247,3 +247,10 @@
     docs(project(":work:work-rxjava3"))
     docs(project(":work:work-testing"))
 }
+
+afterEvaluate {
+    // b/172817047 : exploratory workaround to avoid running two dokkaKotlinDocs tasks at once
+    tasks["dokkaKotlinDocs"].configure { task ->
+        task.mustRunAfter(":docs-public:dokkaKotlinDocs")
+    }
+}
diff --git a/leanback/leanback/lint-baseline.xml b/leanback/leanback/lint-baseline.xml
index 05b53f7..7989b71 100644
--- a/leanback/leanback/lint-baseline.xml
+++ b/leanback/leanback/lint-baseline.xml
@@ -2,534 +2,6 @@
 <issues format="5" by="lint 4.2.0-alpha15" client="gradle" variant="debug" version="4.2.0-alpha15">
 
     <issue
-        id="CustomViewStyleable"
-        message="By convention, the custom view (`BaseCardView`) and the declare-styleable (`lbBaseCardView`) should have the same name (various editor features rely on this convention)"
-        errorLine1="        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbBaseCardView,"
-        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="159"
-            column="62"/>
-    </issue>
-
-    <issue
-        id="CustomViewStyleable"
-        message="By convention, the declare-styleable (`lbBaseCardView_Layout`) for a layout parameter class (`LayoutParams`) is expected to be the surrounding class (`BaseCardView`) plus &quot;`_Layout`&quot;, e.g. `BaseCardView_Layout`. (Various editor features rely on this convention.)"
-        errorLine1="            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.lbBaseCardView_Layout);"
-        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="866"
-            column="60"/>
-    </issue>
-
-    <issue
-        id="CustomViewStyleable"
-        message="By convention, the custom view (`BaseGridView`) and the declare-styleable (`lbBaseGridView`) should have the same name (various editor features rely on this convention)"
-        errorLine1="        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbBaseGridView);"
-        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="285"
-            column="62"/>
-    </issue>
-
-    <issue
-        id="CustomViewStyleable"
-        message="By convention, the custom view (`DatePicker`) and the declare-styleable (`lbDatePicker`) should have the same name (various editor features rely on this convention)"
-        errorLine1="                R.styleable.lbDatePicker);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/picker/DatePicker.java"
-            line="78"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="CustomViewStyleable"
-        message="By convention, the custom view (`HorizontalGridView`) and the declare-styleable (`lbHorizontalGridView`) should have the same name (various editor features rely on this convention)"
-        errorLine1="        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbHorizontalGridView);"
-        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/HorizontalGridView.java"
-            line="84"
-            column="62"/>
-    </issue>
-
-    <issue
-        id="CustomViewStyleable"
-        message="By convention, the custom view (`ImageCardView`) and the declare-styleable (`lbImageCardView`) should have the same name (various editor features rely on this convention)"
-        errorLine1="                R.styleable.lbImageCardView, defStyleAttr, defStyle);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="173"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="CustomViewStyleable"
-        message="By convention, the custom view (`Picker`) and the declare-styleable (`lbPicker`) should have the same name (various editor features rely on this convention)"
-        errorLine1="                attrs, R.styleable.lbPicker, defStyleAttr, 0);"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="196"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="CustomViewStyleable"
-        message="By convention, the custom view (`PinPicker`) and the declare-styleable (`lbPinPicker`) should have the same name (various editor features rely on this convention)"
-        errorLine1="                attrs, R.styleable.lbPinPicker, defStyleAttr, 0);"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/picker/PinPicker.java"
-            line="47"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="CustomViewStyleable"
-        message="By convention, the custom view (`ResizingTextView`) and the declare-styleable (`lbResizingTextView`) should have the same name (various editor features rely on this convention)"
-        errorLine1="        TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.lbResizingTextView,"
-        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/ResizingTextView.java"
-            line="58"
-            column="58"/>
-    </issue>
-
-    <issue
-        id="CustomViewStyleable"
-        message="By convention, the custom view (`SearchOrbView`) and the declare-styleable (`lbSearchOrbView`) should have the same name (various editor features rely on this convention)"
-        errorLine1="        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbSearchOrbView,"
-        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="185"
-            column="62"/>
-    </issue>
-
-    <issue
-        id="CustomViewStyleable"
-        message="By convention, the custom view (`TimePicker`) and the declare-styleable (`lbTimePicker`) should have the same name (various editor features rely on this convention)"
-        errorLine1="                R.styleable.lbTimePicker);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/picker/TimePicker.java"
-            line="110"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="CustomViewStyleable"
-        message="By convention, the custom view (`VerticalGridView`) and the declare-styleable (`lbVerticalGridView`) should have the same name (various editor features rely on this convention)"
-        errorLine1="        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbVerticalGridView);"
-        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/VerticalGridView.java"
-            line="60"
-            column="62"/>
-    </issue>
-
-    <issue
-        id="DefaultLocale"
-        message="Implicitly using the default locale is a common source of bugs: Use `toUpperCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
-        errorLine1="        String dateFieldsPattern = datePickerFormat.toUpperCase();"
-        errorLine2="                                                    ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/picker/DatePicker.java"
-            line="244"
-            column="53"/>
-    </issue>
-
-    <issue
-        id="DefaultLocale"
-        message="Implicitly using the default locale is a common source of bugs: Use `toUpperCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
-        errorLine1="        timeFieldsPattern = timeFieldsPattern.toUpperCase();"
-        errorLine2="                                              ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/picker/TimePicker.java"
-            line="281"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="InlinedApi"
-        message="Field requires API level 26 (current min is 17): `android.view.View#AUTOFILL_TYPE_TEXT`"
-        errorLine1="        return AUTOFILL_TYPE_TEXT;"
-        errorLine2="               ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="133"
-            column="16"/>
-    </issue>
-
-    <issue
-        id="SimpleDateFormat"
-        message="To get local formatting use `getDateInstance()`, `getDateTimeInstance()`, or `getTimeInstance()`, or use `new SimpleDateFormat(String template, Locale locale)` with for example `Locale.US` for ASCII dates."
-        errorLine1="    private final DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);"
-        errorLine2="                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/picker/DatePicker.java"
-            line="60"
-            column="44"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `elevation` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="        android:elevation=&quot;@dimen/lb_details_overview_z&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_details_overview.xml"
-            line="37"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `elevation` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="        android:elevation=&quot;@dimen/lb_details_overview_z&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_fullwidth_details_overview.xml"
-            line="38"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `elevation` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="    android:elevation=&quot;@dimen/lb_details_overview_z&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_fullwidth_details_overview_logo.xml"
-            line="23"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionName` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="    android:transitionName=&quot;guidedactions_root&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedactions.xml"
-            line="20"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="    android:transitionGroup=&quot;false&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedactions.xml"
-            line="21"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionName` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="        android:transitionName=&quot;guidedactions_list_background&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedactions.xml"
-            line="28"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionName` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="        android:transitionName=&quot;guidedactions_content&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedactions.xml"
-            line="38"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="        android:transitionGroup=&quot;false&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedactions.xml"
-            line="39"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="            android:transitionGroup=&quot;true&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedactions.xml"
-            line="43"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionName` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="            android:transitionName=&quot;guidedactions_sub_list_background&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedactions.xml"
-            line="48"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="            android:transitionGroup=&quot;true&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedactions.xml"
-            line="58"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionName` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="    android:transitionName=&quot;guidedactions_root2&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedbuttonactions.xml"
-            line="20"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="    android:transitionGroup=&quot;false&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedbuttonactions.xml"
-            line="21"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionName` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="        android:transitionName=&quot;guidedactions_list_background2&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedbuttonactions.xml"
-            line="28"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionName` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="        android:transitionName=&quot;guidedactions_content2&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedbuttonactions.xml"
-            line="35"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="        android:transitionGroup=&quot;false&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedbuttonactions.xml"
-            line="36"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="            android:transitionGroup=&quot;true&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedbuttonactions.xml"
-            line="40"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionName` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="    android:transitionName=&quot;guidedstep_background&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedstep_background.xml"
-            line="20"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionName` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="                android:transitionName=&quot;action_fragment_root&quot;"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedstep_fragment.xml"
-            line="48"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="                android:transitionGroup=&quot;false&quot;"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedstep_fragment.xml"
-            line="49"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionName` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="                    android:transitionName=&quot;action_fragment_background&quot;"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedstep_fragment.xml"
-            line="60"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `outlineProvider` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="                    android:outlineProvider=&quot;paddedBounds&quot;"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedstep_fragment.xml"
-            line="62"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `elevation` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="                    android:elevation=&quot;?attr/guidedActionsElevation&quot; />"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedstep_fragment.xml"
-            line="66"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionName` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="                    android:transitionName=&quot;action_fragment&quot;"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedstep_fragment.xml"
-            line="72"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="                    android:transitionGroup=&quot;false&quot;"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedstep_fragment.xml"
-            line="73"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `elevation` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="                    android:elevation=&quot;?attr/guidedActionsElevation&quot; />"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_guidedstep_fragment.xml"
-            line="77"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="    android:transitionGroup=&quot;true&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_headers_fragment.xml"
-            line="20"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `elevation` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="    android:elevation=&quot;@dimen/lb_browse_headers_z&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_headers_fragment.xml"
-            line="24"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="        android:transitionGroup=&quot;false&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_playback_fragment.xml"
-            line="21"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="        android:transitionGroup=&quot;false&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_playback_fragment.xml"
-            line="26"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="            android:transitionGroup=&quot;true&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_playback_fragment.xml"
-            line="32"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="UnusedAttribute"
-        message="Attribute `transitionGroup` is only used in API level 21 and higher (current min is 17)"
-        errorLine1="        android:transitionGroup=&quot;true&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/res/layout/lb_title_view.xml"
-            line="41"
-            column="9"/>
-    </issue>
-
-    <issue
         id="PrivateConstructorForUtilityClass"
         message="Utility class with non private constructor"
         errorLine1="public class FocusHighlightHelper {"
@@ -569,7 +41,7 @@
         errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/DatePicker.java"
-            line="142"
+            line="144"
             column="58"/>
     </issue>
 
@@ -943,7 +415,7 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/TimePicker.java"
-            line="157"
+            line="159"
             column="38"/>
     </issue>
 
@@ -1625,7 +1097,7 @@
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/res/layout/lb_guidedstep_background.xml"
-            line="23"
+            line="25"
             column="5"/>
     </issue>
 
@@ -1636,7 +1108,7 @@
         errorLine2="     ~~~~~~~~~~~">
         <location
             file="src/main/res/layout/lb_details_overview.xml"
-            line="32"
+            line="33"
             column="6"/>
     </issue>
 
@@ -1647,7 +1119,7 @@
         errorLine2="     ~~~~~~~~~~~~">
         <location
             file="src/main/res/layout/lb_details_overview.xml"
-            line="41"
+            line="43"
             column="6"/>
     </issue>
 
@@ -1669,7 +1141,7 @@
         errorLine2="     ~~~~~~~~~~~">
         <location
             file="src/main/res/layout/lb_fullwidth_details_overview.xml"
-            line="31"
+            line="32"
             column="6"/>
     </issue>
 
@@ -1680,7 +1152,7 @@
         errorLine2="     ~~~~~~~~~~~~">
         <location
             file="src/main/res/layout/lb_fullwidth_details_overview.xml"
-            line="41"
+            line="43"
             column="6"/>
     </issue>
 
@@ -1691,7 +1163,7 @@
         errorLine2="         ~~~~~~~~~~~~">
         <location
             file="src/main/res/layout/lb_guidedstep_fragment.xml"
-            line="33"
+            line="34"
             column="10"/>
     </issue>
 
@@ -1734,7 +1206,7 @@
         errorLine2="                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="160"
+            line="162"
             column="20"/>
     </issue>
 
@@ -1789,7 +1261,7 @@
         errorLine2="         ~~~~~~~~~">
         <location
             file="src/main/res/layout/lb_details_overview.xml"
-            line="48"
+            line="50"
             column="10"/>
     </issue>
 
@@ -1976,7 +1448,7 @@
         errorLine2="     ~~~~~~~~~">
         <location
             file="src/main/res/layout/lb_title_view.xml"
-            line="19"
+            line="20"
             column="6"/>
     </issue>
 
@@ -2094,7 +1566,7 @@
         errorLine2="                   ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/TimePicker.java"
-            line="434"
+            line="436"
             column="20"/>
     </issue>
 
@@ -2116,7 +1588,7 @@
         errorLine2="                                                                         ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="144"
+            line="145"
             column="74"/>
     </issue>
 
@@ -3051,7 +2523,7 @@
         errorLine2="                        ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="148"
+            line="149"
             column="25"/>
     </issue>
 
@@ -3062,7 +2534,7 @@
         errorLine2="                        ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="152"
+            line="153"
             column="25"/>
     </issue>
 
@@ -3073,7 +2545,7 @@
         errorLine2="                                         ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="152"
+            line="153"
             column="42"/>
     </issue>
 
@@ -3084,7 +2556,7 @@
         errorLine2="                        ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="156"
+            line="158"
             column="25"/>
     </issue>
 
@@ -3095,7 +2567,7 @@
         errorLine2="                                         ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="156"
+            line="158"
             column="42"/>
     </issue>
 
@@ -3106,7 +2578,7 @@
         errorLine2="              ~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="598"
+            line="600"
             column="15"/>
     </issue>
 
@@ -3117,7 +2589,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="819"
+            line="821"
             column="12"/>
     </issue>
 
@@ -3128,7 +2600,7 @@
         errorLine2="                                             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="819"
+            line="821"
             column="46"/>
     </issue>
 
@@ -3139,7 +2611,7 @@
         errorLine2="              ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="824"
+            line="826"
             column="15"/>
     </issue>
 
@@ -3150,7 +2622,7 @@
         errorLine2="              ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="830"
+            line="832"
             column="15"/>
     </issue>
 
@@ -3161,7 +2633,7 @@
         errorLine2="                                                ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="830"
+            line="832"
             column="49"/>
     </issue>
 
@@ -3172,7 +2644,7 @@
         errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="839"
+            line="841"
             column="41"/>
     </issue>
 
@@ -3183,7 +2655,7 @@
         errorLine2="                            ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="864"
+            line="867"
             column="29"/>
     </issue>
 
@@ -3194,7 +2666,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="864"
+            line="867"
             column="40"/>
     </issue>
 
@@ -3205,7 +2677,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="884"
+            line="887"
             column="29"/>
     </issue>
 
@@ -3216,7 +2688,7 @@
         errorLine2="                            ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseCardView.java"
-            line="894"
+            line="897"
             column="29"/>
     </issue>
 
@@ -3315,7 +2787,7 @@
         errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="663"
+            line="664"
             column="43"/>
     </issue>
 
@@ -3326,7 +2798,7 @@
         errorLine2="                                           ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="676"
+            line="677"
             column="44"/>
     </issue>
 
@@ -3337,7 +2809,7 @@
         errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="708"
+            line="709"
             column="54"/>
     </issue>
 
@@ -3348,7 +2820,7 @@
         errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="720"
+            line="721"
             column="54"/>
     </issue>
 
@@ -3359,7 +2831,7 @@
         errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="729"
+            line="730"
             column="57"/>
     </issue>
 
@@ -3370,7 +2842,7 @@
         errorLine2="                                                                    ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="794"
+            line="795"
             column="69"/>
     </issue>
 
@@ -3381,7 +2853,7 @@
         errorLine2="                                                              ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="820"
+            line="821"
             column="63"/>
     </issue>
 
@@ -3392,7 +2864,7 @@
         errorLine2="                                                              ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="899"
+            line="900"
             column="63"/>
     </issue>
 
@@ -3403,7 +2875,7 @@
         errorLine2="                                       ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="915"
+            line="916"
             column="40"/>
     </issue>
 
@@ -3414,7 +2886,7 @@
         errorLine2="                                                  ~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="915"
+            line="916"
             column="51"/>
     </issue>
 
@@ -3425,7 +2897,7 @@
         errorLine2="           ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="929"
+            line="930"
             column="12"/>
     </issue>
 
@@ -3436,7 +2908,7 @@
         errorLine2="                                                                    ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="943"
+            line="944"
             column="69"/>
     </issue>
 
@@ -3447,7 +2919,7 @@
         errorLine2="                                            ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="1039"
+            line="1040"
             column="45"/>
     </issue>
 
@@ -3458,7 +2930,7 @@
         errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="1047"
+            line="1048"
             column="46"/>
     </issue>
 
@@ -3469,7 +2941,7 @@
         errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="1055"
+            line="1056"
             column="43"/>
     </issue>
 
@@ -3480,7 +2952,7 @@
         errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="1063"
+            line="1064"
             column="43"/>
     </issue>
 
@@ -3491,7 +2963,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="1071"
+            line="1072"
             column="12"/>
     </issue>
 
@@ -3502,7 +2974,7 @@
         errorLine2="                                    ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="1076"
+            line="1077"
             column="37"/>
     </issue>
 
@@ -3513,7 +2985,7 @@
         errorLine2="                                      ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="1087"
+            line="1088"
             column="39"/>
     </issue>
 
@@ -3524,7 +2996,7 @@
         errorLine2="                                                  ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="1097"
+            line="1098"
             column="51"/>
     </issue>
 
@@ -3535,7 +3007,7 @@
         errorLine2="                           ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/BaseGridView.java"
-            line="1347"
+            line="1348"
             column="28"/>
     </issue>
 
@@ -6241,7 +5713,7 @@
         errorLine2="                      ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/DatePicker.java"
-            line="68"
+            line="69"
             column="23"/>
     </issue>
 
@@ -6252,7 +5724,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/DatePicker.java"
-            line="68"
+            line="69"
             column="40"/>
     </issue>
 
@@ -6263,7 +5735,7 @@
         errorLine2="                      ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/DatePicker.java"
-            line="72"
+            line="74"
             column="23"/>
     </issue>
 
@@ -6274,7 +5746,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/DatePicker.java"
-            line="72"
+            line="74"
             column="40"/>
     </issue>
 
@@ -6285,7 +5757,7 @@
         errorLine2="                                    ~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/DatePicker.java"
-            line="227"
+            line="229"
             column="37"/>
     </issue>
 
@@ -6296,7 +5768,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/DatePicker.java"
-            line="285"
+            line="287"
             column="12"/>
     </issue>
 
@@ -9970,7 +9442,7 @@
         errorLine2="                               ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="143"
+            line="144"
             column="32"/>
     </issue>
 
@@ -9981,7 +9453,7 @@
         errorLine2="                                                           ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="143"
+            line="144"
             column="60"/>
     </issue>
 
@@ -9992,7 +9464,7 @@
         errorLine2="            ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="144"
+            line="145"
             column="13"/>
     </issue>
 
@@ -10003,7 +9475,7 @@
         errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="144"
+            line="145"
             column="42"/>
     </issue>
 
@@ -10014,7 +9486,7 @@
         errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="166"
+            line="169"
             column="33"/>
     </issue>
 
@@ -10025,7 +9497,7 @@
         errorLine2="                                 ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="175"
+            line="178"
             column="34"/>
     </issue>
 
@@ -10036,7 +9508,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="242"
+            line="245"
             column="12"/>
     </issue>
 
@@ -10047,7 +9519,7 @@
         errorLine2="                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="251"
+            line="254"
             column="24"/>
     </issue>
 
@@ -10058,7 +9530,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="258"
+            line="261"
             column="12"/>
     </issue>
 
@@ -10069,7 +9541,7 @@
         errorLine2="                                 ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="266"
+            line="269"
             column="34"/>
     </issue>
 
@@ -10080,7 +9552,7 @@
         errorLine2="                                 ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="274"
+            line="277"
             column="34"/>
     </issue>
 
@@ -10091,7 +9563,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="283"
+            line="286"
             column="12"/>
     </issue>
 
@@ -10102,7 +9574,7 @@
         errorLine2="           ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="303"
+            line="302"
             column="12"/>
     </issue>
 
@@ -10113,7 +9585,7 @@
         errorLine2="                                         ~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="303"
+            line="302"
             column="42"/>
     </issue>
 
@@ -10124,7 +9596,7 @@
         errorLine2="                                 ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="334"
+            line="333"
             column="34"/>
     </issue>
 
@@ -10135,7 +9607,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="401"
+            line="400"
             column="12"/>
     </issue>
 
@@ -10146,7 +9618,7 @@
         errorLine2="                                                                  ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="401"
+            line="400"
             column="67"/>
     </issue>
 
@@ -10157,7 +9629,7 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="418"
+            line="416"
             column="38"/>
     </issue>
 
@@ -10168,7 +9640,7 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionAdapter.java"
-            line="451"
+            line="449"
             column="38"/>
     </issue>
 
@@ -10344,7 +9816,7 @@
         errorLine2="                                ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="76"
+            line="77"
             column="33"/>
     </issue>
 
@@ -10355,7 +9827,7 @@
         errorLine2="                                ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="80"
+            line="81"
             column="33"/>
     </issue>
 
@@ -10366,7 +9838,7 @@
         errorLine2="                                             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="80"
+            line="81"
             column="46"/>
     </issue>
 
@@ -10377,7 +9849,7 @@
         errorLine2="                                ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="84"
+            line="85"
             column="33"/>
     </issue>
 
@@ -10388,7 +9860,7 @@
         errorLine2="                                             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="84"
+            line="85"
             column="46"/>
     </issue>
 
@@ -10399,7 +9871,7 @@
         errorLine2="                                  ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="92"
+            line="93"
             column="35"/>
     </issue>
 
@@ -10410,7 +9882,7 @@
         errorLine2="                                            ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="97"
+            line="98"
             column="45"/>
     </issue>
 
@@ -10421,7 +9893,7 @@
         errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="109"
+            line="110"
             column="51"/>
     </issue>
 
@@ -10432,7 +9904,7 @@
         errorLine2="                                                                  ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="115"
+            line="116"
             column="67"/>
     </issue>
 
@@ -10443,7 +9915,7 @@
         errorLine2="                                      ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="137"
+            line="139"
             column="39"/>
     </issue>
 
@@ -10454,7 +9926,7 @@
         errorLine2="                         ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="142"
+            line="144"
             column="26"/>
     </issue>
 
@@ -10465,7 +9937,7 @@
         errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/GuidedActionEditText.java"
-            line="154"
+            line="156"
             column="54"/>
     </issue>
 
@@ -12555,7 +12027,7 @@
         errorLine2="                              ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/HorizontalGridView.java"
-            line="68"
+            line="69"
             column="31"/>
     </issue>
 
@@ -12566,7 +12038,7 @@
         errorLine2="                              ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/HorizontalGridView.java"
-            line="72"
+            line="73"
             column="31"/>
     </issue>
 
@@ -12577,7 +12049,7 @@
         errorLine2="                                               ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/HorizontalGridView.java"
-            line="72"
+            line="73"
             column="48"/>
     </issue>
 
@@ -12588,7 +12060,7 @@
         errorLine2="                              ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/HorizontalGridView.java"
-            line="76"
+            line="77"
             column="31"/>
     </issue>
 
@@ -12599,7 +12071,7 @@
         errorLine2="                                               ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/HorizontalGridView.java"
-            line="76"
+            line="77"
             column="48"/>
     </issue>
 
@@ -12610,7 +12082,7 @@
         errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/HorizontalGridView.java"
-            line="82"
+            line="84"
             column="35"/>
     </issue>
 
@@ -12621,7 +12093,7 @@
         errorLine2="                                                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/HorizontalGridView.java"
-            line="82"
+            line="84"
             column="52"/>
     </issue>
 
@@ -12632,7 +12104,7 @@
         errorLine2="                     ~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/HorizontalGridView.java"
-            line="313"
+            line="315"
             column="22"/>
     </issue>
 
@@ -12698,7 +12170,7 @@
         errorLine2="                         ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="153"
+            line="154"
             column="26"/>
     </issue>
 
@@ -12709,7 +12181,7 @@
         errorLine2="                         ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="160"
+            line="161"
             column="26"/>
     </issue>
 
@@ -12720,7 +12192,7 @@
         errorLine2="                                          ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="160"
+            line="161"
             column="43"/>
     </issue>
 
@@ -12731,7 +12203,7 @@
         errorLine2="                         ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="287"
+            line="289"
             column="26"/>
     </issue>
 
@@ -12742,7 +12214,7 @@
         errorLine2="                         ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="294"
+            line="296"
             column="26"/>
     </issue>
 
@@ -12753,7 +12225,7 @@
         errorLine2="                                          ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="294"
+            line="296"
             column="43"/>
     </issue>
 
@@ -12764,7 +12236,7 @@
         errorLine2="                 ~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="301"
+            line="303"
             column="18"/>
     </issue>
 
@@ -12775,7 +12247,7 @@
         errorLine2="                                      ~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="317"
+            line="319"
             column="39"/>
     </issue>
 
@@ -12786,7 +12258,7 @@
         errorLine2="                             ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="326"
+            line="328"
             column="30"/>
     </issue>
 
@@ -12797,7 +12269,7 @@
         errorLine2="                             ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="333"
+            line="335"
             column="30"/>
     </issue>
 
@@ -12808,7 +12280,7 @@
         errorLine2="           ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="367"
+            line="369"
             column="12"/>
     </issue>
 
@@ -12819,7 +12291,7 @@
         errorLine2="           ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="378"
+            line="380"
             column="12"/>
     </issue>
 
@@ -12830,7 +12302,7 @@
         errorLine2="                                      ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="388"
+            line="390"
             column="39"/>
     </issue>
 
@@ -12841,7 +12313,7 @@
         errorLine2="                             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="406"
+            line="408"
             column="30"/>
     </issue>
 
@@ -12852,7 +12324,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="416"
+            line="418"
             column="12"/>
     </issue>
 
@@ -12863,7 +12335,7 @@
         errorLine2="                               ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="427"
+            line="429"
             column="32"/>
     </issue>
 
@@ -12874,7 +12346,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="437"
+            line="439"
             column="12"/>
     </issue>
 
@@ -12885,7 +12357,7 @@
         errorLine2="                              ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="448"
+            line="450"
             column="31"/>
     </issue>
 
@@ -12896,7 +12368,7 @@
         errorLine2="           ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/ImageCardView.java"
-            line="463"
+            line="465"
             column="12"/>
     </issue>
 
@@ -16240,7 +15712,7 @@
         errorLine2="                            ~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="73"
+            line="74"
             column="29"/>
     </issue>
 
@@ -16251,7 +15723,7 @@
         errorLine2="                 ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="102"
+            line="103"
             column="18"/>
     </issue>
 
@@ -16262,7 +15734,7 @@
         errorLine2="                                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="111"
+            line="112"
             column="36"/>
     </issue>
 
@@ -16273,7 +15745,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="120"
+            line="121"
             column="18"/>
     </issue>
 
@@ -16284,7 +15756,7 @@
         errorLine2="                                    ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="136"
+            line="137"
             column="37"/>
     </issue>
 
@@ -16295,7 +15767,7 @@
         errorLine2="                  ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="186"
+            line="187"
             column="19"/>
     </issue>
 
@@ -16306,7 +15778,7 @@
         errorLine2="                                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="186"
+            line="187"
             column="36"/>
     </issue>
 
@@ -16317,7 +15789,7 @@
         errorLine2="                  ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="193"
+            line="195"
             column="19"/>
     </issue>
 
@@ -16328,7 +15800,7 @@
         errorLine2="                                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="193"
+            line="195"
             column="36"/>
     </issue>
 
@@ -16339,7 +15811,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="228"
+            line="230"
             column="12"/>
     </issue>
 
@@ -16350,7 +15822,7 @@
         errorLine2="                           ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="254"
+            line="256"
             column="28"/>
     </issue>
 
@@ -16361,7 +15833,7 @@
         errorLine2="                                             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="329"
+            line="331"
             column="46"/>
     </issue>
 
@@ -16372,7 +15844,7 @@
         errorLine2="                                          ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="376"
+            line="378"
             column="43"/>
     </issue>
 
@@ -16383,7 +15855,7 @@
         errorLine2="                                             ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="388"
+            line="390"
             column="46"/>
     </issue>
 
@@ -16394,7 +15866,7 @@
         errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="541"
+            line="543"
             column="37"/>
     </issue>
 
@@ -16405,7 +15877,7 @@
         errorLine2="                                                                 ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="560"
+            line="562"
             column="66"/>
     </issue>
 
@@ -16416,7 +15888,7 @@
         errorLine2="                                  ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="696"
+            line="698"
             column="35"/>
     </issue>
 
@@ -16427,7 +15899,7 @@
         errorLine2="                                              ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/Picker.java"
-            line="696"
+            line="698"
             column="47"/>
     </issue>
 
@@ -16493,7 +15965,7 @@
         errorLine2="                     ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/PinPicker.java"
-            line="40"
+            line="41"
             column="22"/>
     </issue>
 
@@ -16504,7 +15976,7 @@
         errorLine2="                                      ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/PinPicker.java"
-            line="40"
+            line="41"
             column="39"/>
     </issue>
 
@@ -16515,7 +15987,7 @@
         errorLine2="                     ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/PinPicker.java"
-            line="44"
+            line="46"
             column="22"/>
     </issue>
 
@@ -16526,7 +15998,7 @@
         errorLine2="                                      ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/PinPicker.java"
-            line="44"
+            line="46"
             column="39"/>
     </issue>
 
@@ -16537,7 +16009,7 @@
         errorLine2="                                    ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/PinPicker.java"
-            line="87"
+            line="89"
             column="37"/>
     </issue>
 
@@ -16548,7 +16020,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/PinPicker.java"
-            line="104"
+            line="106"
             column="12"/>
     </issue>
 
@@ -21696,7 +21168,7 @@
         errorLine2="                         ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="155"
+            line="156"
             column="26"/>
     </issue>
 
@@ -21707,7 +21179,7 @@
         errorLine2="                         ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="159"
+            line="160"
             column="26"/>
     </issue>
 
@@ -21718,7 +21190,7 @@
         errorLine2="                                          ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="159"
+            line="160"
             column="43"/>
     </issue>
 
@@ -21729,7 +21201,7 @@
         errorLine2="                         ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="163"
+            line="165"
             column="26"/>
     </issue>
 
@@ -21740,7 +21212,7 @@
         errorLine2="                                          ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="163"
+            line="165"
             column="43"/>
     </issue>
 
@@ -21751,7 +21223,7 @@
         errorLine2="                        ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="228"
+            line="230"
             column="25"/>
     </issue>
 
@@ -21762,7 +21234,7 @@
         errorLine2="                                                                    ~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="248"
+            line="250"
             column="69"/>
     </issue>
 
@@ -21773,7 +21245,7 @@
         errorLine2="                           ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="264"
+            line="266"
             column="28"/>
     </issue>
 
@@ -21784,7 +21256,7 @@
         errorLine2="           ~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="273"
+            line="275"
             column="12"/>
     </issue>
 
@@ -21795,7 +21267,7 @@
         errorLine2="                                        ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="281"
+            line="283"
             column="41"/>
     </issue>
 
@@ -21806,7 +21278,7 @@
         errorLine2="                             ~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="317"
+            line="319"
             column="30"/>
     </issue>
 
@@ -21817,7 +21289,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/SearchOrbView.java"
-            line="331"
+            line="333"
             column="12"/>
     </issue>
 
@@ -23137,7 +22609,7 @@
         errorLine2="                      ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/TimePicker.java"
-            line="89"
+            line="90"
             column="23"/>
     </issue>
 
@@ -23148,7 +22620,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/TimePicker.java"
-            line="89"
+            line="90"
             column="40"/>
     </issue>
 
@@ -23159,7 +22631,7 @@
         errorLine2="                      ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/TimePicker.java"
-            line="103"
+            line="105"
             column="23"/>
     </issue>
 
@@ -23170,7 +22642,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/picker/TimePicker.java"
-            line="103"
+            line="105"
             column="40"/>
     </issue>
 
@@ -24985,7 +24457,7 @@
         errorLine2="                            ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/VerticalGridView.java"
-            line="44"
+            line="45"
             column="29"/>
     </issue>
 
@@ -24996,7 +24468,7 @@
         errorLine2="                            ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/VerticalGridView.java"
-            line="48"
+            line="49"
             column="29"/>
     </issue>
 
@@ -25007,7 +24479,7 @@
         errorLine2="                                             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/VerticalGridView.java"
-            line="48"
+            line="49"
             column="46"/>
     </issue>
 
@@ -25018,7 +24490,7 @@
         errorLine2="                            ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/VerticalGridView.java"
-            line="52"
+            line="53"
             column="29"/>
     </issue>
 
@@ -25029,7 +24501,7 @@
         errorLine2="                                             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/VerticalGridView.java"
-            line="52"
+            line="53"
             column="46"/>
     </issue>
 
@@ -25040,7 +24512,7 @@
         errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/VerticalGridView.java"
-            line="58"
+            line="60"
             column="35"/>
     </issue>
 
@@ -25051,7 +24523,7 @@
         errorLine2="                                                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/leanback/widget/VerticalGridView.java"
-            line="58"
+            line="60"
             column="52"/>
     </issue>
 
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/BaseCardView.java b/leanback/leanback/src/main/java/androidx/leanback/widget/BaseCardView.java
index 911a36d..d1c0738 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/BaseCardView.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/BaseCardView.java
@@ -16,6 +16,7 @@
 
 package androidx.leanback.widget;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
@@ -153,6 +154,7 @@
         this(context, attrs, R.attr.baseCardViewStyle);
     }
 
+    @SuppressLint("CustomViewStyleable")
     public BaseCardView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
@@ -861,6 +863,7 @@
         /**
          * {@inheritDoc}
          */
+        @SuppressLint("CustomViewStyleable")
         public LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
             TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.lbBaseCardView_Layout);
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/BaseGridView.java b/leanback/leanback/src/main/java/androidx/leanback/widget/BaseGridView.java
index 16083c8..82864ee 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/BaseGridView.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/BaseGridView.java
@@ -281,6 +281,7 @@
         });
     }
 
+    @SuppressLint("CustomViewStyleable")
     void initBaseGridViewAttributes(Context context, AttributeSet attrs) {
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbBaseGridView);
         boolean throughFront = a.getBoolean(R.styleable.lbBaseGridView_focusOutFront, false);
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionAdapter.java b/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionAdapter.java
index b2ec182..fa28365 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionAdapter.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionAdapter.java
@@ -98,6 +98,7 @@
         void onImeClose();
     }
 
+    final RecyclerView mRecyclerView;
     private final boolean mIsSubAdapter;
     private final ActionOnKeyListener mActionOnKeyListener;
     private final ActionOnFocusListener mActionOnFocusListener;
@@ -113,9 +114,9 @@
     private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            if (v != null && v.getWindowToken() != null && getRecyclerView() != null) {
+            if (v != null && v.getWindowToken() != null && mRecyclerView.isAttachedToWindow()) {
                 GuidedActionsStylist.ViewHolder avh = (GuidedActionsStylist.ViewHolder)
-                        getRecyclerView().getChildViewHolder(v);
+                        mRecyclerView.getChildViewHolder(v);
                 GuidedAction action = avh.getAction();
                 if (action.hasTextEditable()) {
                     if (DEBUG_EDIT) Log.v(TAG_EDIT, "openIme by click");
@@ -155,6 +156,8 @@
         if (!isSubAdapter) {
             mDiffCallback = GuidedActionDiffCallback.getInstance();
         }
+        mRecyclerView = isSubAdapter ? mStylist.getSubActionsGridView() :
+                mStylist.getActionsGridView();
     }
 
     /**
@@ -292,10 +295,6 @@
         return mStylist.getItemViewType(mActions.get(position));
     }
 
-    RecyclerView getRecyclerView() {
-        return mIsSubAdapter ? mStylist.getSubActionsGridView() : mStylist.getActionsGridView();
-    }
-
     /**
      * {@inheritDoc}
      */
@@ -362,8 +361,8 @@
         }
 
         public void unFocus() {
-            if (mSelectedView != null && getRecyclerView() != null) {
-                ViewHolder vh = getRecyclerView().getChildViewHolder(mSelectedView);
+            if (mSelectedView != null && mRecyclerView.isAttachedToWindow()) {
+                ViewHolder vh = mRecyclerView.getChildViewHolder(mSelectedView);
                 if (vh != null) {
                     GuidedActionsStylist.ViewHolder avh = (GuidedActionsStylist.ViewHolder)vh;
                     mStylist.onAnimateItemFocused(avh, false);
@@ -376,11 +375,11 @@
 
         @Override
         public void onFocusChange(View v, boolean hasFocus) {
-            if (getRecyclerView() == null) {
+            if (!mRecyclerView.isAttachedToWindow()) {
                 return;
             }
             GuidedActionsStylist.ViewHolder avh = (GuidedActionsStylist.ViewHolder)
-                    getRecyclerView().getChildViewHolder(v);
+                    mRecyclerView.getChildViewHolder(v);
             if (hasFocus) {
                 mSelectedView = v;
                 if (mFocusListener != null) {
@@ -399,18 +398,17 @@
     }
 
     public GuidedActionsStylist.ViewHolder findSubChildViewHolder(View v) {
-        // Needed because RecyclerView.getChildViewHolder does not traverse the hierarchy
-        if (getRecyclerView() == null) {
+        if (!mRecyclerView.isAttachedToWindow()) {
             return null;
         }
         GuidedActionsStylist.ViewHolder result = null;
         ViewParent parent = v.getParent();
-        while (parent != getRecyclerView() && parent != null && v != null) {
+        while (parent != mRecyclerView && parent != null && v != null) {
             v = (View)parent;
             parent = parent.getParent();
         }
         if (parent != null && v != null) {
-            result = (GuidedActionsStylist.ViewHolder)getRecyclerView().getChildViewHolder(v);
+            result = (GuidedActionsStylist.ViewHolder) mRecyclerView.getChildViewHolder(v);
         }
         return result;
     }
@@ -418,7 +416,7 @@
     public void handleCheckedActions(GuidedActionsStylist.ViewHolder avh) {
         GuidedAction action = avh.getAction();
         int actionCheckSetId = action.getCheckSetId();
-        if (getRecyclerView() != null && actionCheckSetId != GuidedAction.NO_CHECK_SET) {
+        if (mRecyclerView.isAttachedToWindow() && actionCheckSetId != GuidedAction.NO_CHECK_SET) {
             // Find any actions that are checked and are in the same group
             // as the selected action. Fade their checkmarks out.
             if (actionCheckSetId != GuidedAction.CHECKBOX_CHECK_SET_ID) {
@@ -427,7 +425,7 @@
                     if (a != action && a.getCheckSetId() == actionCheckSetId && a.isChecked()) {
                         a.setChecked(false);
                         GuidedActionsStylist.ViewHolder vh = (GuidedActionsStylist.ViewHolder)
-                                getRecyclerView().findViewHolderForPosition(i);
+                                mRecyclerView.findViewHolderForPosition(i);
                         if (vh != null) {
                             mStylist.onAnimateItemChecked(vh, false);
                         }
@@ -466,7 +464,7 @@
          */
         @Override
         public boolean onKey(View v, int keyCode, KeyEvent event) {
-            if (v == null || event == null || getRecyclerView() == null) {
+            if (v == null || event == null || !mRecyclerView.isAttachedToWindow()) {
                 return false;
             }
             boolean handled = false;
@@ -478,7 +476,7 @@
                 case KeyEvent.KEYCODE_ENTER:
 
                     GuidedActionsStylist.ViewHolder avh = (GuidedActionsStylist.ViewHolder)
-                            getRecyclerView().getChildViewHolder(v);
+                            mRecyclerView.getChildViewHolder(v);
                     GuidedAction action = avh.getAction();
 
                     if (!action.isEnabled() || action.infoOnly()) {
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionEditText.java b/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionEditText.java
index 04717c0..5f661df 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionEditText.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionEditText.java
@@ -30,6 +30,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 import androidx.core.widget.TextViewCompat;
 
 /**
@@ -126,6 +127,7 @@
         }
     }
 
+    @RequiresApi(26)
     @Override
     public int getAutofillType() {
         // make it always autofillable as Guided fragment switches InputType when user clicks
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/HorizontalGridView.java b/leanback/leanback/src/main/java/androidx/leanback/widget/HorizontalGridView.java
index 1fe9378..6bda0d3 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/HorizontalGridView.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/HorizontalGridView.java
@@ -13,6 +13,7 @@
  */
 package androidx.leanback.widget;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -79,6 +80,7 @@
         initAttributes(context, attrs);
     }
 
+    @SuppressLint("CustomViewStyleable")
     protected void initAttributes(Context context, AttributeSet attrs) {
         initBaseGridViewAttributes(context, attrs);
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbHorizontalGridView);
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/ImageCardView.java b/leanback/leanback/src/main/java/androidx/leanback/widget/ImageCardView.java
index b70fc03..88f961e 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/ImageCardView.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/ImageCardView.java
@@ -14,6 +14,7 @@
 package androidx.leanback.widget;
 
 import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
@@ -162,6 +163,7 @@
         buildImageCardView(attrs, defStyleAttr, R.style.Widget_Leanback_ImageCardView);
     }
 
+    @SuppressLint("CustomViewStyleable")
     private void buildImageCardView(AttributeSet attrs, int defStyleAttr, int defStyle) {
         // Make sure the ImageCardView is focusable.
         setFocusable(true);
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/ResizingTextView.java b/leanback/leanback/src/main/java/androidx/leanback/widget/ResizingTextView.java
index 805b1b2..e2cc560 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/ResizingTextView.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/ResizingTextView.java
@@ -53,6 +53,7 @@
     private int mDefaultPaddingTop;
     private int mDefaultPaddingBottom;
 
+    @SuppressLint("CustomViewStyleable")
     public ResizingTextView(Context ctx, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(ctx, attrs, defStyleAttr);
         TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.lbResizingTextView,
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/SearchOrbView.java b/leanback/leanback/src/main/java/androidx/leanback/widget/SearchOrbView.java
index 84e7957..b54540f 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/SearchOrbView.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/SearchOrbView.java
@@ -18,6 +18,7 @@
 
 import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -160,6 +161,7 @@
         this(context, attrs, R.attr.searchOrbViewStyle);
     }
 
+    @SuppressLint("CustomViewStyleable")
     public SearchOrbView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/VerticalGridView.java b/leanback/leanback/src/main/java/androidx/leanback/widget/VerticalGridView.java
index 981d13a..44a38d6 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/VerticalGridView.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/VerticalGridView.java
@@ -13,6 +13,7 @@
  */
 package androidx.leanback.widget;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
@@ -55,6 +56,7 @@
         initAttributes(context, attrs);
     }
 
+    @SuppressLint("CustomViewStyleable")
     protected void initAttributes(Context context, AttributeSet attrs) {
         initBaseGridViewAttributes(context, attrs);
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbVerticalGridView);
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/picker/DatePicker.java b/leanback/leanback/src/main/java/androidx/leanback/widget/picker/DatePicker.java
index f2d25af..7ce9b57 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/picker/DatePicker.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/picker/DatePicker.java
@@ -14,6 +14,7 @@
 
 package androidx.leanback.widget.picker;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.text.TextUtils;
@@ -57,7 +58,7 @@
     private int mColYearIndex;
 
     private static final String DATE_FORMAT = "MM/dd/yyyy";
-    private final DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
+    private final DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.getDefault());
     private PickerUtility.DateConstant mConstant;
 
     private Calendar mMinDate;
@@ -69,6 +70,7 @@
         this(context, attrs, R.attr.datePickerStyle);
     }
 
+    @SuppressLint("CustomViewStyleable")
     public DatePicker(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
@@ -241,7 +243,7 @@
         setSeparators(separators);
         mYearColumn = mMonthColumn = mDayColumn = null;
         mColYearIndex = mColDayIndex = mColMonthIndex = -1;
-        String dateFieldsPattern = datePickerFormat.toUpperCase();
+        String dateFieldsPattern = datePickerFormat.toUpperCase(mConstant.locale);
         ArrayList<PickerColumn> columns = new ArrayList<>(3);
         for (int i = 0; i < dateFieldsPattern.length(); i++) {
             switch (dateFieldsPattern.charAt(i)) {
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/picker/Picker.java b/leanback/leanback/src/main/java/androidx/leanback/widget/picker/Picker.java
index 521ddbd..bb159d9 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/picker/Picker.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/picker/Picker.java
@@ -14,6 +14,7 @@
 
 package androidx.leanback.widget.picker;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
@@ -190,6 +191,7 @@
     /**
      * Creates a Picker widget.
      */
+    @SuppressLint("CustomViewStyleable")
     public Picker(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         final TypedArray a = context.obtainStyledAttributes(
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/picker/PinPicker.java b/leanback/leanback/src/main/java/androidx/leanback/widget/picker/PinPicker.java
index 864eda9..b865af8 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/picker/PinPicker.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/picker/PinPicker.java
@@ -16,6 +16,7 @@
 
 package androidx.leanback.widget.picker;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
@@ -41,6 +42,7 @@
         this(context, attrs, R.attr.pinPickerStyle);
     }
 
+    @SuppressLint("CustomViewStyleable")
     public PinPicker(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         final TypedArray a = context.obtainStyledAttributes(
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/picker/TimePicker.java b/leanback/leanback/src/main/java/androidx/leanback/widget/picker/TimePicker.java
index faf7ecb..3cc9f88 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/picker/TimePicker.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/picker/TimePicker.java
@@ -16,6 +16,7 @@
 
 package androidx.leanback.widget.picker;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.text.TextUtils;
@@ -100,6 +101,7 @@
      *                     resource that supplies default values for the widget. Can be 0 to not
      *                     look for defaults.
      */
+    @SuppressLint("CustomViewStyleable")
     public TimePicker(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
@@ -278,7 +280,7 @@
                     + " the size of timeFieldsPattern: " + timeFieldsPattern.length() + " + 1");
         }
         setSeparators(separators);
-        timeFieldsPattern = timeFieldsPattern.toUpperCase();
+        timeFieldsPattern = timeFieldsPattern.toUpperCase(mConstant.locale);
 
         mHourColumn = mMinuteColumn = mAmPmColumn = null;
         mColHourIndex = mColMinuteIndex = mColAmPmIndex = -1;
diff --git a/leanback/leanback/src/main/res/layout/lb_details_overview.xml b/leanback/leanback/src/main/res/layout/lb_details_overview.xml
index 22d72dc..3ca9e61 100644
--- a/leanback/leanback/src/main/res/layout/lb_details_overview.xml
+++ b/leanback/leanback/src/main/res/layout/lb_details_overview.xml
@@ -17,6 +17,7 @@
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:lb="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:paddingStart="@dimen/lb_details_overview_margin_start"
@@ -35,6 +36,7 @@
         android:layout_height="@dimen/lb_details_overview_height_large"
         android:foreground="#ffffff"
         android:elevation="@dimen/lb_details_overview_z"
+        tools:ignore="UnusedAttribute"
         >
 
     <!-- Background is applied to this inner layout -->
diff --git a/leanback/leanback/src/main/res/layout/lb_fullwidth_details_overview.xml b/leanback/leanback/src/main/res/layout/lb_fullwidth_details_overview.xml
index b6bc902..2e60acc 100644
--- a/leanback/leanback/src/main/res/layout/lb_fullwidth_details_overview.xml
+++ b/leanback/leanback/src/main/res/layout/lb_fullwidth_details_overview.xml
@@ -17,6 +17,7 @@
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:lb="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/details_root"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -36,6 +37,7 @@
         android:clipToPadding="false"
         android:background="?attr/defaultBrandColor"
         android:elevation="@dimen/lb_details_overview_z"
+        tools:ignore="UnusedAttribute"
         >
 
     <LinearLayout
diff --git a/leanback/leanback/src/main/res/layout/lb_fullwidth_details_overview_logo.xml b/leanback/leanback/src/main/res/layout/lb_fullwidth_details_overview_logo.xml
index 3b4cd7e..fa24c51 100644
--- a/leanback/leanback/src/main/res/layout/lb_fullwidth_details_overview_logo.xml
+++ b/leanback/leanback/src/main/res/layout/lb_fullwidth_details_overview_logo.xml
@@ -16,10 +16,12 @@
 -->
 
 <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/details_overview_image"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:adjustViewBounds="true"
     android:elevation="@dimen/lb_details_overview_z"
+    tools:ignore="UnusedAttribute"
     android:maxWidth="@dimen/lb_details_v2_logo_max_width"
     android:maxHeight="@dimen/lb_details_v2_logo_max_height" />
diff --git a/leanback/leanback/src/main/res/layout/lb_guidedactions.xml b/leanback/leanback/src/main/res/layout/lb_guidedactions.xml
index 53122a4..2314005 100644
--- a/leanback/leanback/src/main/res/layout/lb_guidedactions.xml
+++ b/leanback/leanback/src/main/res/layout/lb_guidedactions.xml
@@ -16,9 +16,11 @@
 -->
 <!-- Layout for the settings list fragment -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/guidedactions_root"
     android:transitionName="guidedactions_root"
     android:transitionGroup="false"
+    tools:ignore="UnusedAttribute"
     android:layout_width="0dp"
     android:layout_weight="1"
     android:layout_height="match_parent">
@@ -26,6 +28,7 @@
     <androidx.leanback.widget.NonOverlappingView
         android:id="@+id/guidedactions_list_background"
         android:transitionName="guidedactions_list_background"
+        tools:ignore="UnusedAttribute"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:visibility="gone"
@@ -37,15 +40,18 @@
         android:id="@+id/guidedactions_content"
         android:transitionName="guidedactions_content"
         android:transitionGroup="false"
+        tools:ignore="UnusedAttribute"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
         <androidx.leanback.widget.VerticalGridView
             android:transitionGroup="true"
+            tools:ignore="UnusedAttribute"
             android:id="@+id/guidedactions_list"
             style="?attr/guidedActionsListStyle" />
         <androidx.leanback.widget.NonOverlappingView
             android:id="@+id/guidedactions_sub_list_background"
             android:transitionName="guidedactions_sub_list_background"
+            tools:ignore="UnusedAttribute"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_alignTop="@+id/guidedactions_sub_list"
@@ -56,6 +62,7 @@
             android:background="?attr/guidedActionsBackgroundDark" />
         <androidx.leanback.widget.VerticalGridView
             android:transitionGroup="true"
+            tools:ignore="UnusedAttribute"
             android:id="@+id/guidedactions_sub_list"
             android:layout_width="match_parent"
             android:layout_height="0dp"
diff --git a/leanback/leanback/src/main/res/layout/lb_guidedbuttonactions.xml b/leanback/leanback/src/main/res/layout/lb_guidedbuttonactions.xml
index 5678fdf..fe91320 100644
--- a/leanback/leanback/src/main/res/layout/lb_guidedbuttonactions.xml
+++ b/leanback/leanback/src/main/res/layout/lb_guidedbuttonactions.xml
@@ -17,8 +17,10 @@
 <!-- Layout for the settings list fragment -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/guidedactions_root2"
+    xmlns:tools="http://schemas.android.com/tools"
     android:transitionName="guidedactions_root2"
     android:transitionGroup="false"
+    tools:ignore="UnusedAttribute"
     android:layout_width="0dp"
     android:layout_weight="?attr/guidedButtonActionsWidthWeight"
     android:layout_height="match_parent">
@@ -26,6 +28,7 @@
     <androidx.leanback.widget.NonOverlappingView
         android:id="@+id/guidedactions_list_background2"
         android:transitionName="guidedactions_list_background2"
+        tools:ignore="UnusedAttribute"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:background="?attr/guidedActionsBackgroundDark" />
@@ -34,6 +37,7 @@
         android:id="@+id/guidedactions_content2"
         android:transitionName="guidedactions_content2"
         android:transitionGroup="false"
+        tools:ignore="UnusedAttribute"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
         <androidx.leanback.widget.VerticalGridView
diff --git a/leanback/leanback/src/main/res/layout/lb_guidedstep_background.xml b/leanback/leanback/src/main/res/layout/lb_guidedstep_background.xml
index 7332f9c..1ab32dd 100644
--- a/leanback/leanback/src/main/res/layout/lb_guidedstep_background.xml
+++ b/leanback/leanback/src/main/res/layout/lb_guidedstep_background.xml
@@ -16,8 +16,10 @@
 -->
 <androidx.leanback.widget.NonOverlappingView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/guidedstep_background"
     android:transitionName="guidedstep_background"
+    tools:ignore="UnusedAttribute"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="?attr/guidedStepBackground" />
diff --git a/leanback/leanback/src/main/res/layout/lb_guidedstep_fragment.xml b/leanback/leanback/src/main/res/layout/lb_guidedstep_fragment.xml
index 0df9991..766dbcc 100644
--- a/leanback/leanback/src/main/res/layout/lb_guidedstep_fragment.xml
+++ b/leanback/leanback/src/main/res/layout/lb_guidedstep_fragment.xml
@@ -17,6 +17,7 @@
 <!-- Layout for the frame of a 2 pane actions fragment. -->
 <androidx.leanback.app.GuidedStepRootLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/guidedstep_root"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -47,6 +48,7 @@
                 android:id="@+id/action_fragment_root"
                 android:transitionName="action_fragment_root"
                 android:transitionGroup="false"
+                tools:ignore="UnusedAttribute"
                 android:orientation="horizontal"
                 android:clipToPadding="false"
                 android:clipChildren="false"
@@ -58,6 +60,7 @@
                 <androidx.leanback.widget.NonOverlappingView
                     android:id="@+id/action_fragment_background"
                     android:transitionName="action_fragment_background"
+                    tools:ignore="UnusedAttribute"
                     android:orientation="horizontal"
                     android:outlineProvider="paddedBounds"
                     android:layout_width="match_parent"
@@ -71,6 +74,7 @@
                     android:descendantFocusability="afterDescendants"
                     android:transitionName="action_fragment"
                     android:transitionGroup="false"
+                    tools:ignore="UnusedAttribute"
                     android:orientation="horizontal"
                     android:layout_width="match_parent"
                     android:layout_height="match_parent"
diff --git a/leanback/leanback/src/main/res/layout/lb_headers_fragment.xml b/leanback/leanback/src/main/res/layout/lb_headers_fragment.xml
index 5876a36..8f15719d 100644
--- a/leanback/leanback/src/main/res/layout/lb_headers_fragment.xml
+++ b/leanback/leanback/src/main/res/layout/lb_headers_fragment.xml
@@ -16,8 +16,10 @@
 -->
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/browse_headers_root"
     android:transitionGroup="true"
+    tools:ignore="UnusedAttribute"
     android:layout_width="@dimen/lb_browse_headers_width"
     android:layout_height="match_parent"
     android:paddingEnd="@dimen/lb_browse_header_padding_end"
diff --git a/leanback/leanback/src/main/res/layout/lb_playback_fragment.xml b/leanback/leanback/src/main/res/layout/lb_playback_fragment.xml
index 19ebe90..f116b5f 100644
--- a/leanback/leanback/src/main/res/layout/lb_playback_fragment.xml
+++ b/leanback/leanback/src/main/res/layout/lb_playback_fragment.xml
@@ -16,20 +16,24 @@
 -->
 <FrameLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
         android:id="@+id/playback_fragment_root"
         android:layout_width="match_parent"
         android:transitionGroup="false"
+        tools:ignore="UnusedAttribute"
         android:layout_height="match_parent">
 
     <androidx.leanback.widget.NonOverlappingFrameLayout
         android:id="@+id/playback_fragment_background"
         android:transitionGroup="false"
+        tools:ignore="UnusedAttribute"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
     <androidx.leanback.widget.NonOverlappingFrameLayout
             android:id="@+id/playback_controls_dock"
             android:transitionGroup="true"
+            tools:ignore="UnusedAttribute"
             android:layout_height="match_parent"
             android:layout_width="match_parent"/>
 </FrameLayout>
diff --git a/leanback/leanback/src/main/res/layout/lb_title_view.xml b/leanback/leanback/src/main/res/layout/lb_title_view.xml
index ae439d3..2971ebc 100644
--- a/leanback/leanback/src/main/res/layout/lb_title_view.xml
+++ b/leanback/leanback/src/main/res/layout/lb_title_view.xml
@@ -14,7 +14,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android" >
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
 
     <ImageView
         android:id="@+id/title_badge"
@@ -39,6 +40,7 @@
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         android:transitionGroup="true"
+        tools:ignore="UnusedAttribute"
         android:layout_gravity="center_vertical|start"
         android:visibility="invisible" />
 
diff --git a/media/version-compat-tests/current/client/build.gradle b/media/version-compat-tests/current/client/build.gradle
index d6925c2..8961d3a 100644
--- a/media/version-compat-tests/current/client/build.gradle
+++ b/media/version-compat-tests/current/client/build.gradle
@@ -23,7 +23,7 @@
 
 dependencies {
     androidTestImplementation(project(":media:media"))
-    androidTestImplementation(project(":support-media-test-lib"))
+    androidTestImplementation(project(":media:version-compat-tests:lib"))
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/media/version-compat-tests/current/service/build.gradle b/media/version-compat-tests/current/service/build.gradle
index d6925c2..8961d3a 100644
--- a/media/version-compat-tests/current/service/build.gradle
+++ b/media/version-compat-tests/current/service/build.gradle
@@ -23,7 +23,7 @@
 
 dependencies {
     androidTestImplementation(project(":media:media"))
-    androidTestImplementation(project(":support-media-test-lib"))
+    androidTestImplementation(project(":media:version-compat-tests:lib"))
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/media/version-compat-tests/previous/client/build.gradle b/media/version-compat-tests/previous/client/build.gradle
index b0055a4..f32aaa4 100644
--- a/media/version-compat-tests/previous/client/build.gradle
+++ b/media/version-compat-tests/previous/client/build.gradle
@@ -22,7 +22,7 @@
 }
 
 dependencies {
-    androidTestImplementation project(':support-media-test-lib')
+    androidTestImplementation project(':media:version-compat-tests:lib')
     androidTestImplementation "com.android.support:support-media-compat:27.1.0"
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/media/version-compat-tests/previous/service/build.gradle b/media/version-compat-tests/previous/service/build.gradle
index e089e44..9edfd60 100644
--- a/media/version-compat-tests/previous/service/build.gradle
+++ b/media/version-compat-tests/previous/service/build.gradle
@@ -22,7 +22,7 @@
 }
 
 dependencies {
-    androidTestImplementation(project(":support-media-test-lib"))
+    androidTestImplementation(project(":media:version-compat-tests:lib"))
     androidTestImplementation "com.android.support:support-media-compat:27.1.0"
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java b/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java
index 5eecc32..f54cc41 100644
--- a/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java
+++ b/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java
@@ -602,6 +602,8 @@
      * completed.
      * <p>
      * On success, a {@link PlayerResult} should be returned with {@code item} removed.
+     * <p>
+     * If the last item is removed, the player should be moved to {@link #PLAYER_STATE_IDLE}.
      *
      * @param index the index of the item you want to remove in the playlist
      * @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
diff --git a/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java b/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java
index d822f5e..b61ea0b 100644
--- a/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java
+++ b/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java
@@ -1596,6 +1596,37 @@
 
     @Test
     @MediumTest
+    public void setPlaylistAndRemoveAll_playerShouldMoveIdleState() throws Exception {
+        List<MediaItem> playlist = new ArrayList<>();
+        playlist.add(createMediaItem(R.raw.number1));
+
+        final TestUtils.Monitor onPlayerStateChangedCalled = new TestUtils.Monitor();
+        final AtomicInteger playerState = new AtomicInteger();
+
+        MediaPlayer.PlayerCallback callback = new MediaPlayer.PlayerCallback() {
+            @Override
+            public void onPlayerStateChanged(@NonNull SessionPlayer player, int state) {
+                playerState.set(state);
+                onPlayerStateChangedCalled.signal();
+            }
+        };
+
+        mPlayer.registerPlayerCallback(mExecutor, callback);
+
+        assertNotNull(mPlayer.setPlaylist(playlist, null));
+        assertNotNull(mPlayer.prepare());
+        do {
+            assertTrue(onPlayerStateChangedCalled.waitForSignal(1000));
+        } while (playerState.get() != MediaPlayer.PLAYER_STATE_PAUSED);
+
+        mPlayer.removePlaylistItem(0);
+        do {
+            assertTrue(onPlayerStateChangedCalled.waitForSignal(1000));
+        } while (playerState.get() != MediaPlayer.PLAYER_STATE_IDLE);
+    }
+
+    @Test
+    @MediumTest
     public void skipToPlaylistItems() throws Exception {
         int listSize = 5;
         List<MediaItem> playlist = createPlaylist(listSize);
diff --git a/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java b/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
index 87a5e81..9a44910 100644
--- a/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
+++ b/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
@@ -1375,7 +1375,16 @@
                 });
 
                 ArrayList<ResolvableFuture<PlayerResult>> futures = new ArrayList<>();
-                if (updatedCurNextItem != null) {
+                if (curItem == null) {
+                    resetInternal();
+                    notifySessionPlayerCallback(new SessionPlayerCallbackNotifier() {
+                        @Override
+                        public void callCallback(SessionPlayer.PlayerCallback callback) {
+                            callback.onPlayerStateChanged(MediaPlayer.this, PLAYER_STATE_IDLE);
+                        }
+                    });
+                    futures.add(createFutureForResultCode(RESULT_SUCCESS));
+                } else if (updatedCurNextItem != null) {
                     if (updatedCurNextItem.first != null) {
                         futures.addAll(setMediaItemsInternal(curItem, nextItem));
                     } else if (updatedCurNextItem.second != null) {
@@ -2006,20 +2015,7 @@
             }
             mPendingFutures.clear();
         }
-        synchronized (mStateLock) {
-            mState = PLAYER_STATE_IDLE;
-            mMediaItemToBuffState.clear();
-        }
-        synchronized (mPlaylistLock) {
-            mPlaylist.clear();
-            mShuffledList.clear();
-            mCurPlaylistItem = null;
-            mNextPlaylistItem = null;
-            mCurrentShuffleIdx = END_OF_PLAYLIST;
-            mSetMediaItemCalled = false;
-        }
-        mAudioFocusHandler.onReset();
-        mPlayer.reset();
+        resetInternal();
     }
 
     /**
@@ -2158,9 +2154,6 @@
         return new VideoSize(mPlayer.getVideoWidth(), mPlayer.getVideoHeight());
     }
 
-
-
-
     /**
      * @return a {@link PersistableBundle} containing the set of attributes and values
      * available for the media being handled by this player instance.
@@ -3026,6 +3019,24 @@
         return futures;
     }
 
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    void resetInternal() {
+        synchronized (mStateLock) {
+            mState = PLAYER_STATE_IDLE;
+            mMediaItemToBuffState.clear();
+        }
+        synchronized (mPlaylistLock) {
+            mPlaylist.clear();
+            mShuffledList.clear();
+            mCurPlaylistItem = null;
+            mNextPlaylistItem = null;
+            mCurrentShuffleIdx = END_OF_PLAYLIST;
+            mSetMediaItemCalled = false;
+        }
+        mAudioFocusHandler.onReset();
+        mPlayer.reset();
+    }
+
     private ResolvableFuture<PlayerResult> setMediaItemInternal(MediaItem item) {
         ResolvableFuture<PlayerResult> future = ResolvableFuture.create();
         synchronized (mPendingCommands) {
@@ -3126,7 +3137,7 @@
     Pair<MediaItem, MediaItem> updateAndGetCurrentNextItemIfNeededLocked() {
         MediaItem changedCurItem = null;
         MediaItem changedNextItem = null;
-        if (mCurrentShuffleIdx < 0) {
+        if (mCurrentShuffleIdx < 0 || mPlaylist.isEmpty()) {
             if (mCurPlaylistItem == null && mNextPlaylistItem == null) {
                 return null;
             }
diff --git a/media2/session/version-compat-tests/current/client/build.gradle b/media2/session/version-compat-tests/current/client/build.gradle
index 51b16b8..a71c527 100644
--- a/media2/session/version-compat-tests/current/client/build.gradle
+++ b/media2/session/version-compat-tests/current/client/build.gradle
@@ -23,7 +23,7 @@
 
 dependencies {
     androidTestImplementation(project(":media2:media2-session"))
-    androidTestImplementation(project(":support-media2-test-common"))
+    androidTestImplementation(project(":media2:media2-session:version-compat-tests:common"))
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/media2/session/version-compat-tests/current/service/build.gradle b/media2/session/version-compat-tests/current/service/build.gradle
index 6b66631..cebe889 100644
--- a/media2/session/version-compat-tests/current/service/build.gradle
+++ b/media2/session/version-compat-tests/current/service/build.gradle
@@ -23,7 +23,7 @@
 
 dependencies {
     androidTestImplementation(project(":media2:media2-session"))
-    androidTestImplementation(project(":support-media2-test-common"))
+    androidTestImplementation(project(":media2:media2-session:version-compat-tests:common"))
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/media2/session/version-compat-tests/previous/client/build.gradle b/media2/session/version-compat-tests/previous/client/build.gradle
index b36a3c0..3fd0d17 100644
--- a/media2/session/version-compat-tests/previous/client/build.gradle
+++ b/media2/session/version-compat-tests/previous/client/build.gradle
@@ -23,7 +23,7 @@
 
 dependencies {
     androidTestImplementation("androidx.media2:media2-session:1.0.0-beta02")
-    androidTestImplementation(project(":support-media2-test-common"))
+    androidTestImplementation(project(":media2:media2-session:version-compat-tests:common"))
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/media2/session/version-compat-tests/previous/service/build.gradle b/media2/session/version-compat-tests/previous/service/build.gradle
index cb5a4cf..aa5cba0 100644
--- a/media2/session/version-compat-tests/previous/service/build.gradle
+++ b/media2/session/version-compat-tests/previous/service/build.gradle
@@ -23,7 +23,7 @@
 
 dependencies {
     androidTestImplementation("androidx.media2:media2-session:1.0.0-beta02")
-    androidTestImplementation(project(":support-media2-test-common"))
+    androidTestImplementation(project(":media2:media2-session:version-compat-tests:common"))
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/mediarouter/mediarouter/build.gradle b/mediarouter/mediarouter/build.gradle
index 44ed352..1b27ddf 100644
--- a/mediarouter/mediarouter/build.gradle
+++ b/mediarouter/mediarouter/build.gradle
@@ -37,7 +37,7 @@
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
-    androidTestImplementation project(':support-media-test-lib')
+    androidTestImplementation project(':media:version-compat-tests:lib')
 }
 
 android {
diff --git a/navigation/navigation-common/build.gradle b/navigation/navigation-common/build.gradle
index a290ab4..52998f0 100644
--- a/navigation/navigation-common/build.gradle
+++ b/navigation/navigation-common/build.gradle
@@ -31,6 +31,7 @@
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.1.0")
 
+    api(KOTLIN_STDLIB)
     testImplementation(JUNIT)
     testImplementation(MOCKITO_CORE)
     testImplementation(TRUTH)
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/FloatingWindow.java b/navigation/navigation-common/src/main/java/androidx/navigation/FloatingWindow.kt
similarity index 63%
rename from navigation/navigation-common/src/main/java/androidx/navigation/FloatingWindow.java
rename to navigation/navigation-common/src/main/java/androidx/navigation/FloatingWindow.kt
index 9ce6e88..834bc9d 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/FloatingWindow.java
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/FloatingWindow.kt
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
-package androidx.navigation;
+package androidx.navigation
 
 /**
- * A marker interface for {@link NavDestination} subclasses that float above the view of other
- * destinations (i.e. {@link androidx.navigation.fragment.DialogFragmentNavigator.Destination}).
+ * A marker interface for [NavDestination] subclasses that float above the view of other
+ * destinations (i.e. [androidx.navigation.fragment.DialogFragmentNavigator.Destination]).
  *
- * <p>Destinations that implement this interface will automatically be popped off the back
+ *
+ * Destinations that implement this interface will automatically be popped off the back
  * stack when you navigate to a new destination.
  *
- * <p>{@link androidx.navigation.NavController.OnDestinationChangedListener} instances can also
+ *
+ * [androidx.navigation.NavController.OnDestinationChangedListener] instances can also
  * customize their behavior based on whether the destination is a FloatingWindow.
  */
-public interface FloatingWindow {
-}
+public interface FloatingWindow
diff --git a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigator.java b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigator.java
index 3505e37..d199c52 100644
--- a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigator.java
+++ b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/DialogFragmentNavigator.java
@@ -38,6 +38,8 @@
 import androidx.navigation.Navigator;
 import androidx.navigation.NavigatorProvider;
 
+import java.util.HashSet;
+
 /**
  * Navigator that uses {@link DialogFragment#show(FragmentManager, String)}. Every
  * destination using this Navigator must set a valid DialogFragment class name with
@@ -52,6 +54,7 @@
     private final Context mContext;
     private final FragmentManager mFragmentManager;
     private int mDialogCount = 0;
+    private final HashSet<String> mRestoredTagsAwaitingAttach = new HashSet<>();
 
     private LifecycleEventObserver mObserver = new LifecycleEventObserver() {
         @Override
@@ -145,13 +148,20 @@
                 if (fragment != null) {
                     fragment.getLifecycle().addObserver(mObserver);
                 } else {
-                    throw new IllegalStateException("DialogFragment " + index
-                            + " doesn't exist in the FragmentManager");
+                    mRestoredTagsAwaitingAttach.add(DIALOG_TAG + index);
                 }
             }
         }
     }
 
+    // TODO: Switch to FragmentOnAttachListener once we depend on Fragment 1.3
+    void onAttachFragment(@NonNull Fragment childFragment) {
+        boolean needToAddObserver = mRestoredTagsAwaitingAttach.remove(childFragment.getTag());
+        if (needToAddObserver) {
+            childFragment.getLifecycle().addObserver(mObserver);
+        }
+    }
+
     /**
      * NavDestination specific to {@link DialogFragmentNavigator}.
      */
diff --git a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.java b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.java
index 34c3abf..744d5f4 100644
--- a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.java
+++ b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.java
@@ -306,6 +306,16 @@
         navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
     }
 
+    // TODO: DialogFragmentNavigator should use FragmentOnAttachListener from Fragment 1.3
+    @SuppressWarnings("deprecation")
+    @Override
+    public void onAttachFragment(@NonNull Fragment childFragment) {
+        super.onAttachFragment(childFragment);
+        DialogFragmentNavigator dialogFragmentNavigator =
+                mNavController.getNavigatorProvider().getNavigator(DialogFragmentNavigator.class);
+        dialogFragmentNavigator.onAttachFragment(childFragment);
+    }
+
     @CallSuper
     @Override
     public void onPrimaryNavigationFragmentChanged(boolean isPrimaryNavigationFragment) {
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.java b/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.java
index b15a912..96500ef 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.java
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.java
@@ -21,11 +21,13 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.net.Uri;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.Log;
 
 import androidx.annotation.CallSuper;
 import androidx.annotation.NonNull;
@@ -50,6 +52,8 @@
     private static final String EXTRA_POP_EXIT_ANIM =
             "android-support-navigation:ActivityNavigator:popExitAnim";
 
+    private static final String LOG_TAG = "ActivityNavigator";
+
     private Context mContext;
     private Activity mHostActivity;
 
@@ -160,10 +164,23 @@
         }
         final int destId = destination.getId();
         intent.putExtra(EXTRA_NAV_CURRENT, destId);
+        Resources resources = getContext().getResources();
         if (navOptions != null) {
-            // For use in applyPopAnimationsToPendingTransition()
-            intent.putExtra(EXTRA_POP_ENTER_ANIM, navOptions.getPopEnterAnim());
-            intent.putExtra(EXTRA_POP_EXIT_ANIM, navOptions.getPopExitAnim());
+            int popEnterAnim = navOptions.getPopEnterAnim();
+            int popExitAnim = navOptions.getPopExitAnim();
+            if ((popEnterAnim != -1
+                    && resources.getResourceTypeName(popEnterAnim).equals("animator"))
+                    || (popExitAnim != -1
+                    && resources.getResourceTypeName(popExitAnim).equals("animator"))) {
+                Log.w(LOG_TAG, "Activity destinations do not support Animator resource. Ignoring "
+                        + "popEnter resource " + resources.getResourceName(popEnterAnim) + " and "
+                        + "popExit resource " + resources.getResourceName(popExitAnim) + "when "
+                        + "launching " + destination);
+            } else {
+                // For use in applyPopAnimationsToPendingTransition()
+                intent.putExtra(EXTRA_POP_ENTER_ANIM, popEnterAnim);
+                intent.putExtra(EXTRA_POP_EXIT_ANIM, popExitAnim);
+            }
         }
         if (navigatorExtras instanceof Extras) {
             Extras extras = (Extras) navigatorExtras;
@@ -180,9 +197,18 @@
             int enterAnim = navOptions.getEnterAnim();
             int exitAnim = navOptions.getExitAnim();
             if (enterAnim != -1 || exitAnim != -1) {
-                enterAnim = enterAnim != -1 ? enterAnim : 0;
-                exitAnim = exitAnim != -1 ? exitAnim : 0;
-                mHostActivity.overridePendingTransition(enterAnim, exitAnim);
+                if (resources.getResourceTypeName(enterAnim).equals("animator")
+                        || resources.getResourceTypeName(exitAnim).equals("animator")
+                ) {
+                    Log.w(LOG_TAG, "Activity destinations do not support Animator resource. "
+                            + "Ignoring " + "enter resource " + resources.getResourceName(enterAnim)
+                            + " and exit resource " + resources.getResourceName(exitAnim) + "when "
+                            + "launching " + destination);
+                } else {
+                    enterAnim = enterAnim != -1 ? enterAnim : 0;
+                    exitAnim = exitAnim != -1 ? exitAnim : 0;
+                    mHostActivity.overridePendingTransition(enterAnim, exitAnim);
+                }
             }
         }
 
diff --git a/navigation/navigation-safe-args-generator/build.gradle b/navigation/navigation-safe-args-generator/build.gradle
index 3eb4daf..54cdb37 100644
--- a/navigation/navigation-safe-args-generator/build.gradle
+++ b/navigation/navigation-safe-args-generator/build.gradle
@@ -32,7 +32,7 @@
     implementation(KOTLIN_STDLIB)
 
     implementation(JAVAPOET)
-    implementation "com.squareup:kotlinpoet:1.6.0"
+    implementation "com.squareup:kotlinpoet:1.7.2"
 
     testImplementation(JUNIT)
     testImplementation(GOOGLE_COMPILE_TESTING)
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/FunFragmentDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/FunFragmentDirections.kt
index af9c0b8..4e1ef8c 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/FunFragmentDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/FunFragmentDirections.kt
@@ -4,8 +4,8 @@
 import androidx.navigation.ActionOnlyNavDirections
 import androidx.navigation.NavDirections
 
-class FunFragmentDirections private constructor() {
-  companion object {
-    fun next(): NavDirections = ActionOnlyNavDirections(R.id.next)
+public class FunFragmentDirections private constructor() {
+  public companion object {
+    public fun next(): NavDirections = ActionOnlyNavDirections(R.id.next)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/LongPackageFragmentDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/LongPackageFragmentDirections.kt
index f9cd0ae..d244af3 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/LongPackageFragmentDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/LongPackageFragmentDirections.kt
@@ -4,8 +4,8 @@
 import androidx.navigation.ActionOnlyNavDirections
 import androidx.navigation.NavDirections
 
-class LongPackageFragmentDirections private constructor() {
-  companion object {
-    fun next(): NavDirections = ActionOnlyNavDirections(R.id.next)
+public class LongPackageFragmentDirections private constructor() {
+  public companion object {
+    public fun next(): NavDirections = ActionOnlyNavDirections(R.id.next)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/MainFragmentArgs.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/MainFragmentArgs.kt
index 447ecf2..379e28a 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/MainFragmentArgs.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/MainFragmentArgs.kt
@@ -7,7 +7,7 @@
 import java.io.Serializable
 import java.lang.IllegalArgumentException
 import java.lang.UnsupportedOperationException
-import java.nio.file.AccessMode
+import java.nio.`file`.AccessMode
 import kotlin.Array
 import kotlin.Boolean
 import kotlin.Float
@@ -17,20 +17,20 @@
 import kotlin.Suppress
 import kotlin.jvm.JvmStatic
 
-data class MainFragmentArgs(
-  val main: String,
-  val optional: Int = -1,
-  val reference: Int = R.drawable.background,
-  val referenceZeroDefaultValue: Int = 0,
-  val floatArg: Float = 1F,
-  val floatArrayArg: FloatArray,
-  val objectArrayArg: Array<ActivityInfo>,
-  val boolArg: Boolean = true,
-  val optionalParcelable: ActivityInfo? = null,
-  val enumArg: AccessMode = AccessMode.READ
+public data class MainFragmentArgs(
+  public val main: String,
+  public val optional: Int = -1,
+  public val reference: Int = R.drawable.background,
+  public val referenceZeroDefaultValue: Int = 0,
+  public val floatArg: Float = 1F,
+  public val floatArrayArg: FloatArray,
+  public val objectArrayArg: Array<ActivityInfo>,
+  public val boolArg: Boolean = true,
+  public val optionalParcelable: ActivityInfo? = null,
+  public val enumArg: AccessMode = AccessMode.READ
 ) : NavArgs {
   @Suppress("CAST_NEVER_SUCCEEDS")
-  fun toBundle(): Bundle {
+  public fun toBundle(): Bundle {
     val result = Bundle()
     result.putString("main", this.main)
     result.putInt("optional", this.optional)
@@ -53,10 +53,10 @@
     return result
   }
 
-  companion object {
+  public companion object {
     @JvmStatic
     @Suppress("UNCHECKED_CAST")
-    fun fromBundle(bundle: Bundle): MainFragmentArgs {
+    public fun fromBundle(bundle: Bundle): MainFragmentArgs {
       bundle.setClassLoader(MainFragmentArgs::class.java.classLoader)
       val __main : String?
       if (bundle.containsKey("main")) {
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/MainFragmentDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/MainFragmentDirections.kt
index e5912fb..70eb55c 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/MainFragmentDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/MainFragmentDirections.kt
@@ -6,14 +6,14 @@
 import kotlin.Int
 import kotlin.String
 
-class MainFragmentDirections private constructor() {
+public class MainFragmentDirections private constructor() {
   private data class Next(
-    val main: String,
-    val optional: String = "bla"
+    public val main: String,
+    public val optional: String = "bla"
   ) : NavDirections {
-    override fun getActionId(): Int = R.id.next
+    public override fun getActionId(): Int = R.id.next
 
-    override fun getArguments(): Bundle {
+    public override fun getArguments(): Bundle {
       val result = Bundle()
       result.putString("main", this.main)
       result.putString("optional", this.optional)
@@ -21,9 +21,9 @@
     }
   }
 
-  companion object {
-    fun previous(): NavDirections = ActionOnlyNavDirections(R.id.previous)
+  public companion object {
+    public fun previous(): NavDirections = ActionOnlyNavDirections(R.id.previous)
 
-    fun next(main: String, optional: String = "bla"): NavDirections = Next(main, optional)
+    public fun next(main: String, optional: String = "bla"): NavDirections = Next(main, optional)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/Next.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/Next.kt
index 272d94c..0358452 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/Next.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/Next.kt
@@ -11,18 +11,18 @@
 import kotlin.Suppress
 
 private data class Next(
-  val main: String,
-  val mainInt: Int,
-  val optional: String = "bla",
-  val optionalInt: Int = 239,
-  val optionalParcelable: ActivityInfo? = null,
-  val parcelable: ActivityInfo,
-  val innerData: ActivityInfo.WindowLayout
+  public val main: String,
+  public val mainInt: Int,
+  public val optional: String = "bla",
+  public val optionalInt: Int = 239,
+  public val optionalParcelable: ActivityInfo? = null,
+  public val parcelable: ActivityInfo,
+  public val innerData: ActivityInfo.WindowLayout
 ) : NavDirections {
-  override fun getActionId(): Int = R.id.next
+  public override fun getActionId(): Int = R.id.next
 
   @Suppress("CAST_NEVER_SUCCEEDS")
-  override fun getArguments(): Bundle {
+  public override fun getArguments(): Bundle {
     val result = Bundle()
     result.putString("main", this.main)
     result.putInt("mainInt", this.mainInt)
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongNameMainFragmentArgs.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongNameMainFragmentArgs.kt
index 6208f47..ab530ca 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongNameMainFragmentArgs.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/kotlin_nav_writer_test/ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongNameMainFragmentArgs.kt
@@ -4,17 +4,17 @@
 import androidx.navigation.NavArgs
 import kotlin.jvm.JvmStatic
 
-data class
+public data class
     ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongNameFragmentArgs
     : NavArgs {
-  fun toBundle(): Bundle {
+  public fun toBundle(): Bundle {
     val result = Bundle()
     return result
   }
 
-  companion object {
+  public companion object {
     @JvmStatic
-    fun fromBundle(bundle: Bundle):
+    public fun fromBundle(bundle: Bundle):
         ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongNameFragmentArgs {
       bundle.setClassLoader(ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongNameFragmentArgs::class.java.classLoader)
       return ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongNameFragmentArgs()
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/LoginDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/LoginDirections.kt
index 8c5779c..9db5a05 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/LoginDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/LoginDirections.kt
@@ -3,8 +3,8 @@
 import androidx.navigation.ActionOnlyNavDirections
 import androidx.navigation.NavDirections
 
-class LoginDirections private constructor() {
-  companion object {
-    fun actionDone(): NavDirections = ActionOnlyNavDirections(R.id.action_done)
+public class LoginDirections private constructor() {
+  public companion object {
+    public fun actionDone(): NavDirections = ActionOnlyNavDirections(R.id.action_done)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/LoginFragmentDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/LoginFragmentDirections.kt
index 2f8e844..bf2e570 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/LoginFragmentDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/LoginFragmentDirections.kt
@@ -5,10 +5,10 @@
 import foo.LoginDirections
 import foo.R
 
-class LoginFragmentDirections private constructor() {
-  companion object {
-    fun register(): NavDirections = ActionOnlyNavDirections(R.id.register)
+public class LoginFragmentDirections private constructor() {
+  public companion object {
+    public fun register(): NavDirections = ActionOnlyNavDirections(R.id.register)
 
-    fun actionDone(): NavDirections = LoginDirections.actionDone()
+    public fun actionDone(): NavDirections = LoginDirections.actionDone()
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/MainFragmentDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/MainFragmentDirections.kt
index 392b4a0..12006eb 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/MainFragmentDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/MainFragmentDirections.kt
@@ -4,8 +4,8 @@
 import androidx.navigation.NavDirections
 import foo.R
 
-class MainFragmentDirections private constructor() {
-  companion object {
-    fun startLogin(): NavDirections = ActionOnlyNavDirections(R.id.start_login)
+public class MainFragmentDirections private constructor() {
+  public companion object {
+    public fun startLogin(): NavDirections = ActionOnlyNavDirections(R.id.start_login)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/RegisterFragmentDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/RegisterFragmentDirections.kt
index fa5ad14..35314de 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/RegisterFragmentDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested/RegisterFragmentDirections.kt
@@ -3,8 +3,8 @@
 import androidx.navigation.NavDirections
 import foo.LoginDirections
 
-class RegisterFragmentDirections private constructor() {
-  companion object {
-    fun actionDone(): NavDirections = LoginDirections.actionDone()
+public class RegisterFragmentDirections private constructor() {
+  public companion object {
+    public fun actionDone(): NavDirections = LoginDirections.actionDone()
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/InnerSettingsDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/InnerSettingsDirections.kt
index 66ea217..1790afe 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/InnerSettingsDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/InnerSettingsDirections.kt
@@ -5,22 +5,23 @@
 import kotlin.Int
 import kotlin.String
 
-class InnerSettingsDirections private constructor() {
+public class InnerSettingsDirections private constructor() {
   private data class Exit(
-    val exitReason: Int
+    public val exitReason: Int
   ) : NavDirections {
-    override fun getActionId(): Int = R.id.exit
+    public override fun getActionId(): Int = R.id.exit
 
-    override fun getArguments(): Bundle {
+    public override fun getArguments(): Bundle {
       val result = Bundle()
       result.putInt("exitReason", this.exitReason)
       return result
     }
   }
 
-  companion object {
-    fun exit(exitReason: Int): NavDirections = Exit(exitReason)
+  public companion object {
+    public fun exit(exitReason: Int): NavDirections = Exit(exitReason)
 
-    fun main(enterReason: String = "DEFAULT"): NavDirections = SettingsDirections.main(enterReason)
+    public fun main(enterReason: String = "DEFAULT"): NavDirections =
+        SettingsDirections.main(enterReason)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/InnerSettingsFragmentDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/InnerSettingsFragmentDirections.kt
index 0a9b8b4..af5d028 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/InnerSettingsFragmentDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/InnerSettingsFragmentDirections.kt
@@ -5,11 +5,11 @@
 import kotlin.Int
 import kotlin.String
 
-class InnerSettingsFragmentDirections private constructor() {
-  companion object {
-    fun exit(exitReason: Int): NavDirections = InnerSettingsDirections.exit(exitReason)
+public class InnerSettingsFragmentDirections private constructor() {
+  public companion object {
+    public fun exit(exitReason: Int): NavDirections = InnerSettingsDirections.exit(exitReason)
 
-    fun main(enterReason: String = "DEFAULT"): NavDirections =
+    public fun main(enterReason: String = "DEFAULT"): NavDirections =
         InnerSettingsDirections.main(enterReason)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/MainFragmentDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/MainFragmentDirections.kt
index 392b4a0..12006eb 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/MainFragmentDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/MainFragmentDirections.kt
@@ -4,8 +4,8 @@
 import androidx.navigation.NavDirections
 import foo.R
 
-class MainFragmentDirections private constructor() {
-  companion object {
-    fun startLogin(): NavDirections = ActionOnlyNavDirections(R.id.start_login)
+public class MainFragmentDirections private constructor() {
+  public companion object {
+    public fun startLogin(): NavDirections = ActionOnlyNavDirections(R.id.start_login)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/SettingsDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/SettingsDirections.kt
index 3697f81..25f9192 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/SettingsDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/SettingsDirections.kt
@@ -5,13 +5,13 @@
 import kotlin.Int
 import kotlin.String
 
-class SettingsDirections private constructor() {
+public class SettingsDirections private constructor() {
   private data class Main(
-    val enterReason: String = "DEFAULT"
+    public val enterReason: String = "DEFAULT"
   ) : NavDirections {
-    override fun getActionId(): Int = R.id.main
+    public override fun getActionId(): Int = R.id.main
 
-    override fun getArguments(): Bundle {
+    public override fun getArguments(): Bundle {
       val result = Bundle()
       result.putString("enterReason", this.enterReason)
       return result
@@ -19,20 +19,20 @@
   }
 
   private data class Exit(
-    val exitReason: String = "DEFAULT"
+    public val exitReason: String = "DEFAULT"
   ) : NavDirections {
-    override fun getActionId(): Int = R.id.exit
+    public override fun getActionId(): Int = R.id.exit
 
-    override fun getArguments(): Bundle {
+    public override fun getArguments(): Bundle {
       val result = Bundle()
       result.putString("exitReason", this.exitReason)
       return result
     }
   }
 
-  companion object {
-    fun main(enterReason: String = "DEFAULT"): NavDirections = Main(enterReason)
+  public companion object {
+    public fun main(enterReason: String = "DEFAULT"): NavDirections = Main(enterReason)
 
-    fun exit(exitReason: String = "DEFAULT"): NavDirections = Exit(exitReason)
+    public fun exit(exitReason: String = "DEFAULT"): NavDirections = Exit(exitReason)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/SettingsFragmentDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/SettingsFragmentDirections.kt
index f1f0ab7..0e0f0ad 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/SettingsFragmentDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_overridden_action/SettingsFragmentDirections.kt
@@ -4,10 +4,12 @@
 import foo.SettingsDirections
 import kotlin.String
 
-class SettingsFragmentDirections private constructor() {
-  companion object {
-    fun main(enterReason: String = "DEFAULT"): NavDirections = SettingsDirections.main(enterReason)
+public class SettingsFragmentDirections private constructor() {
+  public companion object {
+    public fun main(enterReason: String = "DEFAULT"): NavDirections =
+        SettingsDirections.main(enterReason)
 
-    fun exit(exitReason: String = "DEFAULT"): NavDirections = SettingsDirections.exit(exitReason)
+    public fun exit(exitReason: String = "DEFAULT"): NavDirections =
+        SettingsDirections.exit(exitReason)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/MainFragmentDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/MainFragmentDirections.kt
index 392b4a0..12006eb 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/MainFragmentDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/MainFragmentDirections.kt
@@ -4,8 +4,8 @@
 import androidx.navigation.NavDirections
 import foo.R
 
-class MainFragmentDirections private constructor() {
-  companion object {
-    fun startLogin(): NavDirections = ActionOnlyNavDirections(R.id.start_login)
+public class MainFragmentDirections private constructor() {
+  public companion object {
+    public fun startLogin(): NavDirections = ActionOnlyNavDirections(R.id.start_login)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/SettingsDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/SettingsDirections.kt
index 11801b9..e8c8d17 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/SettingsDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/SettingsDirections.kt
@@ -5,20 +5,20 @@
 import kotlin.Int
 import kotlin.String
 
-class SettingsDirections private constructor() {
+public class SettingsDirections private constructor() {
   private data class Exit(
-    val exitReason: String = "DEFAULT"
+    public val exitReason: String = "DEFAULT"
   ) : NavDirections {
-    override fun getActionId(): Int = R.id.exit
+    public override fun getActionId(): Int = R.id.exit
 
-    override fun getArguments(): Bundle {
+    public override fun getArguments(): Bundle {
       val result = Bundle()
       result.putString("exitReason", this.exitReason)
       return result
     }
   }
 
-  companion object {
-    fun exit(exitReason: String = "DEFAULT"): NavDirections = Exit(exitReason)
+  public companion object {
+    public fun exit(exitReason: String = "DEFAULT"): NavDirections = Exit(exitReason)
   }
 }
diff --git a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/SettingsFragmentDirections.kt b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/SettingsFragmentDirections.kt
index 458d1e5..dee9a87 100644
--- a/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/SettingsFragmentDirections.kt
+++ b/navigation/navigation-safe-args-generator/src/test/test-data/expected/nav_generator_test/kotlin/nested_same_action/SettingsFragmentDirections.kt
@@ -6,20 +6,20 @@
 import kotlin.Int
 import kotlin.String
 
-class SettingsFragmentDirections private constructor() {
+public class SettingsFragmentDirections private constructor() {
   private data class Exit(
-    val exitReason: String = "DIFFERENT"
+    public val exitReason: String = "DIFFERENT"
   ) : NavDirections {
-    override fun getActionId(): Int = R.id.exit
+    public override fun getActionId(): Int = R.id.exit
 
-    override fun getArguments(): Bundle {
+    public override fun getArguments(): Bundle {
       val result = Bundle()
       result.putString("exitReason", this.exitReason)
       return result
     }
   }
 
-  companion object {
-    fun exit(exitReason: String = "DIFFERENT"): NavDirections = Exit(exitReason)
+  public companion object {
+    public fun exit(exitReason: String = "DIFFERENT"): NavDirections = Exit(exitReason)
   }
 }
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.java b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.java
index e08f76a..4609bef 100644
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.java
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.java
@@ -30,6 +30,7 @@
 import androidx.appcompat.widget.Toolbar;
 import androidx.coordinatorlayout.widget.CoordinatorLayout;
 import androidx.customview.widget.Openable;
+import androidx.navigation.ActivityNavigator;
 import androidx.navigation.NavController;
 import androidx.navigation.NavDestination;
 import androidx.navigation.NavGraph;
@@ -73,11 +74,20 @@
     public static boolean onNavDestinationSelected(@NonNull MenuItem item,
             @NonNull NavController navController) {
         NavOptions.Builder builder = new NavOptions.Builder()
-                .setLaunchSingleTop(true)
-                .setEnterAnim(R.animator.nav_default_enter_anim)
-                .setExitAnim(R.animator.nav_default_exit_anim)
-                .setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
-                .setPopExitAnim(R.animator.nav_default_pop_exit_anim);
+                .setLaunchSingleTop(true);
+        if (navController.getCurrentDestination().getParent().findNode(item.getItemId())
+                instanceof ActivityNavigator.Destination) {
+            builder.setEnterAnim(R.anim.nav_default_enter_anim)
+                    .setExitAnim(R.anim.nav_default_exit_anim)
+                    .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
+                    .setPopExitAnim(R.anim.nav_default_pop_exit_anim);
+
+        } else {
+            builder.setEnterAnim(R.animator.nav_default_enter_anim)
+                    .setExitAnim(R.animator.nav_default_exit_anim)
+                    .setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
+                    .setPopExitAnim(R.animator.nav_default_pop_exit_anim);
+        }
         if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
             builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
         }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt b/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt
index bdd752a..376fb76 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt
@@ -65,42 +65,61 @@
                 @OptIn(ExperimentalPagingApi::class)
                 emit(remoteMediatorAccessor?.initialize() == LAUNCH_INITIAL_REFRESH)
             }
-            .scan(null) {
-                previousGeneration: PageFetcherSnapshot<Key, Value>?, triggerRemoteRefresh ->
-                var pagingSource = generateNewPagingSource(previousGeneration?.pagingSource)
+            .scan(null) { previousGeneration: GenerationInfo<Key, Value>?,
+                triggerRemoteRefresh: Boolean ->
+                var pagingSource = generateNewPagingSource(
+                    previousGeneration?.snapshot?.pagingSource
+                )
                 while (pagingSource.invalid) {
-                    pagingSource = generateNewPagingSource(previousGeneration?.pagingSource)
+                    pagingSource = generateNewPagingSource(
+                        previousGeneration?.snapshot?.pagingSource
+                    )
+                }
+
+                var previousPagingState = previousGeneration?.snapshot?.refreshKeyInfo()
+                // If previous generation was invalidated before anchorPosition was established,
+                // re-use last PagingState that successfully loaded pages and has an anchorPosition.
+                // This prevents rapid invalidation from deleting the anchorPosition if the
+                // previous generation didn't have time to load before getting invalidated. We
+                // check for anchorPosition before overriding to prevent empty PagingState from
+                // overriding a PagingState with pages loaded, but no anchorPosition.
+                if (previousPagingState?.anchorPosition == null &&
+                    previousGeneration?.state?.anchorPosition != null
+                ) {
+                    previousPagingState = previousGeneration.state
                 }
 
                 @OptIn(ExperimentalPagingApi::class)
-                val initialKey: Key? = previousGeneration?.refreshKeyInfo()
-                    ?.let { pagingSource.getRefreshKey(it) }
+                val initialKey: Key? = previousPagingState?.let { pagingSource.getRefreshKey(it) }
                     ?: initialKey
 
-                previousGeneration?.close()
+                previousGeneration?.snapshot?.close()
 
-                PageFetcherSnapshot<Key, Value>(
-                    initialKey = initialKey,
-                    pagingSource = pagingSource,
-                    config = config,
-                    retryFlow = retryChannel.asFlow(),
-                    // Only trigger remote refresh on refresh signals that do not originate from
-                    // initialization or PagingSource invalidation.
-                    triggerRemoteRefresh = triggerRemoteRefresh,
-                    remoteMediatorConnection = remoteMediatorAccessor,
-                    invalidate = this@PageFetcher::refresh
+                GenerationInfo(
+                    snapshot = PageFetcherSnapshot<Key, Value>(
+                        initialKey = initialKey,
+                        pagingSource = pagingSource,
+                        config = config,
+                        retryFlow = retryChannel.asFlow(),
+                        // Only trigger remote refresh on refresh signals that do not originate from
+                        // initialization or PagingSource invalidation.
+                        triggerRemoteRefresh = triggerRemoteRefresh,
+                        remoteMediatorConnection = remoteMediatorAccessor,
+                        invalidate = this@PageFetcher::refresh
+                    ),
+                    state = previousPagingState,
                 )
             }
             .filterNotNull()
             .mapLatest { generation ->
                 val downstreamFlow = if (remoteMediatorAccessor == null) {
-                    generation.pageEventFlow
+                    generation.snapshot.pageEventFlow
                 } else {
-                    generation.injectRemoteEvents(remoteMediatorAccessor)
+                    generation.snapshot.injectRemoteEvents(remoteMediatorAccessor)
                 }
                 PagingData(
                     flow = downstreamFlow,
-                    receiver = PagerUiReceiver(generation, retryChannel)
+                    receiver = PagerUiReceiver(generation.snapshot, retryChannel)
                 )
             }
             .collect { send(it) }
@@ -196,4 +215,9 @@
 
         override fun refresh() = this@PageFetcher.refresh()
     }
+
+    private class GenerationInfo<Key : Any, Value : Any>(
+        val snapshot: PageFetcherSnapshot<Key, Value>,
+        val state: PagingState<Key, Value>?
+    )
 }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagingState.kt b/paging/common/src/main/kotlin/androidx/paging/PagingState.kt
index 053cb41..df2899d 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagingState.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagingState.kt
@@ -126,6 +126,11 @@
      */
     fun lastItemOrNull(): Value? = pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
 
+    override fun toString(): String {
+        return "PagingState(pages=$pages, anchorPosition=$anchorPosition, config=$config, " +
+            "leadingPlaceholderCount=$leadingPlaceholderCount)"
+    }
+
     internal inline fun <T> anchorPositionToPagedIndices(
         anchorPosition: Int,
         block: (pageIndex: Int, index: Int) -> T
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt
index 85855ef..49de1d5 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt
@@ -432,6 +432,128 @@
         assertEquals(1, invalidatesFromAdapter)
         job.cancel()
     }
+
+    @Test
+    fun invalidateBeforeAccessPreservesPagingState() = testScope.runBlockingTest {
+        pauseDispatcher {
+            val config = PagingConfig(
+                pageSize = 1,
+                prefetchDistance = 1,
+                enablePlaceholders = true,
+                initialLoadSize = 3,
+            )
+            val pagingSources = mutableListOf<TestPagingSource>()
+            val pageFetcher = PageFetcher(
+                pagingSourceFactory = {
+                    TestPagingSource(loadDelay = 1000).also {
+                        pagingSources.add(it)
+                    }
+                },
+                initialKey = 50,
+                config = config,
+            )
+
+            lateinit var pagingData: PagingData<Int>
+            val job = launch() {
+                pageFetcher.flow.collectLatest {
+                    pagingData = it
+                    it.flow.collect { }
+                }
+            }
+
+            advanceUntilIdle()
+
+            // Trigger access to allow PagingState to get populated for next generation.
+            pagingData.receiver.accessHint(
+                ViewportHint(
+                    pageOffset = 0,
+                    indexInPage = 1,
+                    presentedItemsBefore = 1,
+                    presentedItemsAfter = 1,
+                    originalPageOffsetFirst = 0,
+                    originalPageOffsetLast = 0,
+                )
+            )
+            advanceUntilIdle()
+
+            // Invalidate first generation, instantiating second generation.
+            pagingSources[0].invalidate()
+
+            // Invalidate second generation before it has a chance to complete initial load.
+            advanceTimeBy(500)
+            pagingSources[1].invalidate()
+
+            // Wait for all non-canceled loads to complete.
+            advanceUntilIdle()
+
+            // Verify 3 generations were instantiated.
+            assertThat(pagingSources.size).isEqualTo(3)
+
+            // First generation should use initialKey.
+            assertThat(pagingSources[0].getRefreshKeyCalls).isEmpty()
+
+            // Second generation should receive getRefreshKey call with state from first generation.
+            assertThat(pagingSources[1].getRefreshKeyCalls).isEqualTo(
+                listOf(
+                    PagingState(
+                        pages = pagingSources[0].loadedPages,
+                        anchorPosition = 51,
+                        config = config,
+                        leadingPlaceholderCount = 50,
+                    )
+                )
+            )
+
+            // Verify second generation was invalidated before any pages loaded.
+            assertThat(pagingSources[1].loadedPages).isEmpty()
+
+            // Third generation should receive getRefreshKey call with state from first generation.
+            assertThat(pagingSources[0].loadedPages.size).isEqualTo(1)
+            assertThat(pagingSources[2].getRefreshKeyCalls).isEqualTo(
+                listOf(
+                    PagingState(
+                        pages = pagingSources[0].loadedPages,
+                        anchorPosition = 51,
+                        config = config,
+                        leadingPlaceholderCount = 50,
+                    )
+                )
+            )
+
+            advanceUntilIdle()
+            // Trigger APPEND in third generation.
+            pagingData.receiver.accessHint(
+                ViewportHint(
+                    pageOffset = 0,
+                    indexInPage = 2,
+                    presentedItemsBefore = 2,
+                    presentedItemsAfter = 0,
+                    originalPageOffsetFirst = 0,
+                    originalPageOffsetLast = 0,
+                )
+            )
+            advanceUntilIdle()
+
+            // Invalidate third generation, instantiating fourth generation with new PagingState.
+            pagingSources[2].invalidate()
+            advanceUntilIdle()
+
+            // Fourth generation should receive getRefreshKey call with state from third generation.
+            assertThat(pagingSources[2].loadedPages.size).isEqualTo(2)
+            assertThat(pagingSources[3].getRefreshKeyCalls).isEqualTo(
+                listOf(
+                    PagingState(
+                        pages = pagingSources[2].loadedPages,
+                        anchorPosition = 53,
+                        config = config,
+                        leadingPlaceholderCount = 51,
+                    )
+                )
+            )
+
+            job.cancel()
+        }
+    }
 }
 
 internal class FetcherState<T : Any>(
diff --git a/paging/compose/build.gradle b/paging/compose/build.gradle
index c32f2f1..58ff167 100644
--- a/paging/compose/build.gradle
+++ b/paging/compose/build.gradle
@@ -38,6 +38,7 @@
     api("androidx.paging:paging-common-ktx:3.0.0-alpha06")
 
     androidTestImplementation project(':ui:ui-test')
+    androidTestImplementation project(':internal-testutils-paging')
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(JUNIT)
     androidTestImplementation(TRUTH)
diff --git a/paging/compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsTest.kt b/paging/compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsTest.kt
index bee1d72..dc85a99 100644
--- a/paging/compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsTest.kt
+++ b/paging/compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsTest.kt
@@ -30,8 +30,10 @@
 import androidx.paging.Pager
 import androidx.paging.PagingConfig
 import androidx.paging.PagingSource
+import androidx.paging.TestPagingSource
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -42,47 +44,30 @@
     @get:Rule
     val rule = createComposeRule()
 
-    val items = (1..10).toList().map { "$it" }
+    val items = (1..10).toList().map { it }
 
-    val pagingSource = object : PagingSource<Int, String>() {
-        override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> {
-            val pageNumber = params.key ?: 0
-            val prevKey = if (pageNumber > 0) pageNumber - 1 else null
-            val nextKey = if (pageNumber < items.size) pageNumber + 1 else null
-            val subList = if (pageNumber < items.size) {
-                items.subList(pageNumber, pageNumber + 1)
-            } else {
-                emptyList()
-            }
-
-            return LoadResult.Page(
-                data = subList,
-                prevKey = prevKey,
-                nextKey = nextKey
-            )
-        }
-    }
-
-    val pager = Pager(
-        PagingConfig(
+    private fun createPager(
+        config: PagingConfig = PagingConfig(
             pageSize = 1,
-            enablePlaceholders = true,
-            maxSize = 200
-        )
-    ) {
-        pagingSource
+            enablePlaceholders = false,
+            maxSize = 200,
+            initialLoadSize = 3,
+            prefetchDistance = 1,
+        ),
+        pagingSourceFactory: () -> PagingSource<Int, Int> = {
+            TestPagingSource(items = items, loadDelay = 0)
+        }
+    ): Pager<Int, Int> {
+        return Pager(config = config, pagingSourceFactory = pagingSourceFactory)
     }
 
     @Test
     fun lazyPagingColumnShowsItems() {
         rule.setContent {
-            val lazyPagingItems = pager.flow.collectAsLazyPagingItems()
+            val lazyPagingItems = createPager().flow.collectAsLazyPagingItems()
             LazyColumn(Modifier.preferredHeight(200.dp)) {
                 items(lazyPagingItems) {
-                    Spacer(
-                        Modifier.preferredHeight(101.dp).fillParentMaxWidth()
-                            .testTag("$it")
-                    )
+                    Spacer(Modifier.preferredHeight(101.dp).fillParentMaxWidth().testTag("$it"))
                 }
             }
         }
@@ -105,7 +90,7 @@
     @Test
     fun lazyPagingColumnShowsIndexedItems() {
         rule.setContent {
-            val lazyPagingItems = pager.flow.collectAsLazyPagingItems()
+            val lazyPagingItems = createPager().flow.collectAsLazyPagingItems()
             LazyColumn(Modifier.preferredHeight(200.dp)) {
                 itemsIndexed(lazyPagingItems) { index, item ->
                     Spacer(
@@ -134,13 +119,10 @@
     @Test
     fun lazyPagingRowShowsItems() {
         rule.setContent {
-            val lazyPagingItems = pager.flow.collectAsLazyPagingItems()
+            val lazyPagingItems = createPager().flow.collectAsLazyPagingItems()
             LazyRow(Modifier.preferredWidth(200.dp)) {
                 items(lazyPagingItems) {
-                    Spacer(
-                        Modifier.preferredWidth(101.dp).fillParentMaxHeight()
-                            .testTag("$it")
-                    )
+                    Spacer(Modifier.preferredWidth(101.dp).fillParentMaxHeight().testTag("$it"))
                 }
             }
         }
@@ -163,7 +145,7 @@
     @Test
     fun lazyPagingRowShowsIndexedItems() {
         rule.setContent {
-            val lazyPagingItems = pager.flow.collectAsLazyPagingItems()
+            val lazyPagingItems = createPager().flow.collectAsLazyPagingItems()
             LazyRow(Modifier.preferredWidth(200.dp)) {
                 itemsIndexed(lazyPagingItems) { index, item ->
                     Spacer(
@@ -188,4 +170,96 @@
         rule.onNodeWithTag("3-4")
             .assertDoesNotExist()
     }
+
+    @Test
+    fun snapshot() {
+        lateinit var lazyPagingItems: LazyPagingItems<Int>
+        rule.setContent {
+            lazyPagingItems = createPager().flow.collectAsLazyPagingItems()
+        }
+
+        // Trigger page fetch until all items are loaded
+        for (i in items.indices) {
+            lazyPagingItems.get(i)
+            rule.waitForIdle()
+        }
+
+        assertThat(lazyPagingItems.snapshot()).isEqualTo(items)
+    }
+
+    @Test
+    fun peek() {
+        lateinit var lazyPagingItems: LazyPagingItems<Int>
+        rule.setContent {
+            lazyPagingItems = createPager().flow.collectAsLazyPagingItems()
+        }
+
+        // Trigger page fetch until all items 0-6 are loaded
+        for (i in 0..4) {
+            lazyPagingItems.get(i)
+            rule.waitForIdle()
+        }
+
+        assertThat(lazyPagingItems.itemCount).isEqualTo(6)
+        for (i in 0..4) {
+            assertThat(lazyPagingItems.peek(i)).isEqualTo(items[i])
+        }
+        // Verify peek does not trigger page fetch.
+        assertThat(lazyPagingItems.itemCount).isEqualTo(6)
+    }
+
+    @Test
+    fun retry() {
+        var factoryCallCount = 0
+        lateinit var pagingSource: TestPagingSource
+        val pager = createPager {
+            factoryCallCount++
+            pagingSource = TestPagingSource(items = items, loadDelay = 0)
+            if (factoryCallCount == 1) {
+                pagingSource.errorNextLoad = true
+            }
+            pagingSource
+        }
+
+        lateinit var lazyPagingItems: LazyPagingItems<Int>
+        rule.setContent {
+            lazyPagingItems = pager.flow.collectAsLazyPagingItems()
+        }
+
+        assertThat(lazyPagingItems.snapshot()).isEmpty()
+
+        lazyPagingItems.retry()
+        rule.waitForIdle()
+
+        assertThat(lazyPagingItems.snapshot()).isNotEmpty()
+        // Verify retry does not invalidate.
+        assertThat(factoryCallCount).isEqualTo(1)
+    }
+
+    @Test
+    fun refresh() {
+        var factoryCallCount = 0
+        lateinit var pagingSource: TestPagingSource
+        val pager = createPager {
+            factoryCallCount++
+            pagingSource = TestPagingSource(items = items, loadDelay = 0)
+            if (factoryCallCount == 1) {
+                pagingSource.errorNextLoad = true
+            }
+            pagingSource
+        }
+
+        lateinit var lazyPagingItems: LazyPagingItems<Int>
+        rule.setContent {
+            lazyPagingItems = pager.flow.collectAsLazyPagingItems()
+        }
+
+        assertThat(lazyPagingItems.snapshot()).isEmpty()
+
+        lazyPagingItems.refresh()
+        rule.waitForIdle()
+
+        assertThat(lazyPagingItems.snapshot()).isNotEmpty()
+        assertThat(factoryCallCount).isEqualTo(2)
+    }
 }
\ No newline at end of file
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
index 8b3859b..268a5f7 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
@@ -63,6 +63,28 @@
 }
 
 /**
+ * Returns the unboxed TypeName for this if it can be unboxed, otherwise, returns this.
+ */
+internal fun TypeName.tryUnbox(): TypeName {
+    return if (isBoxedPrimitive) {
+        unbox()
+    } else {
+        this
+    }
+}
+
+/**
+ * Returns the boxed TypeName for this if it can be unboxed, otherwise, returns this.
+ */
+internal fun TypeName.tryBox(): TypeName {
+    return try {
+        box()
+    } catch (err: AssertionError) {
+        this
+    }
+}
+
+/**
  * Helper class to create overrides for XExecutableElements with final parameters and correct
  * parameter names read from Kotlin Metadata.
  */
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt
index 2105173..05f2e59 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt
@@ -17,6 +17,9 @@
 package androidx.room.compiler.processing.javac.kotlin
 
 import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
 import javax.lang.model.element.Element
 import javax.lang.model.element.ExecutableElement
 import javax.lang.model.element.NestingKind
@@ -95,6 +98,53 @@
         else -> error("Unknown primitive type $this")
     }
 
+// see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2-200
+internal fun String.typeNameFromJvmSignature(): TypeName {
+    check(isNotEmpty())
+    return when (this[0]) {
+        'B' -> TypeName.BYTE
+        'C' -> TypeName.CHAR
+        'D' -> TypeName.DOUBLE
+        'F' -> TypeName.FLOAT
+        'I' -> TypeName.INT
+        'J' -> TypeName.LONG
+        'S' -> TypeName.SHORT
+        'Z' -> TypeName.BOOLEAN
+        'L' -> {
+            val end = lastIndexOf(";")
+            check(end > 0) {
+                "invalid input $this"
+            }
+            val simpleNamesSeparator = lastIndexOf('/')
+            val simpleNamesStart = if (simpleNamesSeparator < 0) {
+                1 // first char is 'L'
+            } else {
+                simpleNamesSeparator + 1
+            }
+            val packageName = if (simpleNamesSeparator < 0) {
+                // no package name
+                ""
+            } else {
+                substring(1, simpleNamesSeparator).replace('/', '.')
+            }
+            val firstSimpleNameSeparator = indexOf('$', startIndex = simpleNamesStart)
+            return if (firstSimpleNameSeparator < 0) {
+                // not nested
+                ClassName.get(packageName, substring(simpleNamesStart, end))
+            } else {
+                // nested class
+                val firstSimpleName = substring(simpleNamesStart, firstSimpleNameSeparator)
+                val restOfSimpleNames = substring(firstSimpleNameSeparator + 1, end)
+                    .split('$')
+                    .toTypedArray()
+                ClassName.get(packageName, firstSimpleName, *restOfSimpleNames)
+            }
+        }
+        '[' -> ArrayTypeName.of(substring(1).typeNameFromJvmSignature())
+        else -> error("unexpected jvm signature $this")
+    }
+}
+
 internal fun TypeMirror.descriptor(): String = accept(JvmDescriptorTypeVisitor, Unit)
 
 @Suppress("unused")
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
index fe0ba08..7a39e00 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
@@ -118,4 +118,8 @@
     return checkNotNull(type?.resolve()) {
         "KSTypeArgument.type should not have been null, please file a bug. $this"
     }
+}
+
+internal fun KSTypeReference.isTypeParameterReference(): Boolean {
+    return this.resolve().declaration is KSTypeParameter
 }
\ No newline at end of file
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
index 6df5430..4e308fb 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
@@ -30,12 +30,12 @@
 ) : XAnnotationBox<T> {
     override fun getAsType(methodName: String): XType? {
         val value = getFieldValue<KSType>(methodName)
-        return value?.let(env::wrap)
+        return value?.let(env::wrapDeclared)
     }
 
     override fun getAsTypeList(methodName: String): List<XType> {
         val values = getFieldValue<List<KSType>>(methodName) ?: return emptyList()
-        return values.map(env::wrap)
+        return values.map(env::wrapDeclared)
     }
 
     override fun <R : Annotation> getAsAnnotationBox(methodName: String): XAnnotationBox<R> {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspDeclaredType.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspDeclaredType.kt
index 8b630ed..85752cd 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspDeclaredType.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspDeclaredType.kt
@@ -18,8 +18,8 @@
 
 import androidx.room.compiler.processing.XDeclaredType
 import androidx.room.compiler.processing.XType
-import com.squareup.javapoet.TypeName
 import com.google.devtools.ksp.symbol.KSType
+import com.squareup.javapoet.TypeName
 
 internal open class KspDeclaredType(
     env: KspProcessingEnv,
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
index 7e2d647..728033e 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
@@ -43,7 +43,12 @@
             resolver = env.resolver,
             functionDeclaration = method.declaration,
             ksType = method.containing.declaration.asStarProjectedType()
-        ).let(env::wrap)
+        ).let {
+            env.wrap(
+                originatingReference = parameter.type!!, // as member of doesn't allow nulls
+                ksType = it
+            )
+        }
     }
 
     override fun asMemberOf(other: XDeclaredType): XType {
@@ -55,7 +60,12 @@
             resolver = env.resolver,
             functionDeclaration = method.declaration,
             ksType = other.ksType
-        ).let(env::wrap)
+        ).let {
+            env.wrap(
+                originatingReference = parameter.type!!, // as member of doesn't allow nulls
+                ksType = it
+            )
+        }
     }
 
     override fun kindName(): String {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
index deb3153..8f8e32d 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
@@ -47,7 +47,10 @@
     }
 
     override val type: XType by lazy {
-        env.wrap(declaration.typeAsMemberOf(env.resolver, containing.type.ksType))
+        env.wrap(
+            originatingReference = declaration.type,
+            ksType = declaration.typeAsMemberOf(env.resolver, containing.type.ksType)
+        )
     }
 
     override fun asMemberOf(other: XDeclaredType): XType {
@@ -56,7 +59,10 @@
         }
         check(other is KspType)
         val asMember = declaration.typeAsMemberOf(env.resolver, other.ksType)
-        return env.wrap(asMember)
+        return env.wrap(
+            originatingReference = declaration.type,
+            ksType = asMember
+        )
     }
 
     fun copyTo(newContaining: KspTypeElement) = KspFieldElement(
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
index 74c2e38..ab01a0b 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
@@ -114,7 +114,7 @@
         override fun isSuspendFunction() = true
 
         override val returnType: XType by lazy {
-            env.wrap(env.resolver.builtIns.anyType.makeNullable())
+            env.wrapDeclared(env.resolver.builtIns.anyType.makeNullable())
         }
 
         override val parameters: List<XExecutableParameterElement>
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt
index cca29f31..218643a 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodType.kt
@@ -52,7 +52,8 @@
     ) : KspMethodType(env, origin, containing) {
         override val returnType: XType by lazy {
             env.wrap(
-                origin.declaration.returnTypeAsMemberOf(
+                originatingReference = origin.declaration.returnType!!,
+                ksType = origin.declaration.returnTypeAsMemberOf(
                     resolver = env.resolver,
                     ksType = containing.ksType
                 )
@@ -70,8 +71,9 @@
             get() = origin.returnType
 
         override fun getSuspendFunctionReturnType(): XType {
-            return env.wrap(
-                origin.declaration.returnTypeAsMemberOf(
+            // suspend functions work w/ continuation so it is always declared
+            return env.wrapDeclared(
+                ksType = origin.declaration.returnTypeAsMemberOf(
                     resolver = env.resolver,
                     ksType = containing.ksType
                 )
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspPrimitiveType.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspPrimitiveType.kt
new file mode 100644
index 0000000..21af989
--- /dev/null
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspPrimitiveType.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.ksp
+
+import com.google.devtools.ksp.symbol.KSType
+import com.squareup.javapoet.TypeName
+
+/**
+ * This tries to mimic primitive types in Kotlin.
+ *
+ * Primitiveness of a type cannot always be driven from itself (e.g. its nullability).
+ * For instance, a kotlin.Int might be non-null but still be non primitive if it is derived from a
+ * generic type argument or is part of type parameters.
+ */
+internal class KspPrimitiveType(
+    env: KspProcessingEnv,
+    ksType: KSType
+) : KspType(env, ksType) {
+    override val typeName: TypeName by lazy {
+        // TODO once we generate type names in java realm, this custom conversion won't be necessary
+        // for now, temporarily, we only do conversion here in place
+        return@lazy checkNotNull(
+            KspTypeMapper.getPrimitiveJavaTypeName(
+                ksType.declaration.qualifiedName!!.asString()
+            )
+        ) {
+            "Internal error. Should've found a java primitive for " +
+                "${ksType.declaration.qualifiedName}"
+        }
+    }
+}
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
index 41d2747..c752c97 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
@@ -24,6 +24,7 @@
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.compiler.processing.javac.XTypeElementStore
+import com.google.devtools.ksp.getClassDeclarationByName
 import com.google.devtools.ksp.processing.CodeGenerator
 import com.google.devtools.ksp.processing.KSPLogger
 import com.google.devtools.ksp.processing.Resolver
@@ -32,6 +33,7 @@
 import com.google.devtools.ksp.symbol.KSTypeArgument
 import com.google.devtools.ksp.symbol.KSTypeParameter
 import com.google.devtools.ksp.symbol.KSTypeReference
+import com.google.devtools.ksp.symbol.Nullability
 import com.google.devtools.ksp.symbol.Variance
 
 internal class KspProcessingEnv(
@@ -44,7 +46,7 @@
     private val typeElementStore =
         XTypeElementStore { qName ->
             resolver.getClassDeclarationByName(
-                resolver.getKSNameFromString(qName)
+                KspTypeMapper.swapWithKotlinType(qName)
             )?.let {
                 KspTypeElement(
                     env = this,
@@ -64,8 +66,12 @@
     }
 
     override fun findType(qName: String): XType? {
-        return resolver.findClass(qName)?.let {
-            wrap(it.asStarProjectedType())
+        val kotlinTypeName = KspTypeMapper.swapWithKotlinType(qName)
+        return resolver.findClass(kotlinTypeName)?.let {
+            wrap(
+                allowPrimitives = KspTypeMapper.isJavaPrimitiveType(qName),
+                ksType = it.asStarProjectedType()
+            )
         }
     }
 
@@ -90,7 +96,7 @@
                 variance = Variance.INVARIANT
             )
         }
-        return wrap(
+        return wrapDeclared(
             type.declaration.asType(typeArguments)
         )
     }
@@ -112,21 +118,44 @@
         )
     }
 
-    fun wrap(ksType: KSType): KspDeclaredType {
-        return if (ksType.declaration.qualifiedName?.asString() == KOTLIN_ARRAY_Q_NAME) {
-            KspArrayType(
-                env = this,
-                ksType = ksType
-            )
-        } else {
-            KspDeclaredType(this, ksType)
-        }
+    /**
+     * Wraps the given `ksType` as a [KspDeclaredType].
+     * For the edge cases where `ksType` potentially represents a java primitive, it is always
+     * boxed.
+     */
+    fun wrapDeclared(ksType: KSType): KspDeclaredType {
+        return wrap(
+            ksType = ksType,
+            allowPrimitives = false
+        ) as KspDeclaredType
     }
 
-    fun wrap(ksTypeReference: KSTypeReference): KspDeclaredType {
-        return wrap(ksTypeReference.resolve())
+    /**
+     * Wraps the given `ksType`.
+     *
+     * The [originatingReference] is used to calculate whether the given [ksType] can be a
+     * primitive or not.
+     */
+    fun wrap(
+        originatingReference: KSTypeReference,
+        ksType: KSType
+    ): KspType {
+        return wrap(
+            ksType = ksType,
+            allowPrimitives = !originatingReference.isTypeParameterReference()
+        )
     }
 
+    /**
+     * Wraps the given [typeReference] in to a [KspType].
+     */
+    fun wrap(
+        typeReference: KSTypeReference
+    ) = wrap(
+        originatingReference = typeReference,
+        ksType = typeReference.resolve()
+    )
+
     fun wrap(ksTypeParam: KSTypeParameter, ksTypeArgument: KSTypeArgument): KspTypeArgumentType {
         return KspTypeArgumentType(
             env = this,
@@ -135,6 +164,33 @@
         )
     }
 
+    /**
+     * Wraps the given KSType into a KspType.
+     *
+     * Certain Kotlin types might be primitives in Java but such information cannot be derived
+     * just by looking at the type itself.
+     * Instead, it is passed in an argument to this function and public wrap functions make that
+     * decision.
+     */
+    private fun wrap(ksType: KSType, allowPrimitives: Boolean): KspType {
+        val qName = ksType.declaration.qualifiedName?.asString()
+        if (allowPrimitives && qName != null && ksType.nullability == Nullability.NOT_NULL) {
+            // check for primitives
+            val javaPrimitive = KspTypeMapper.getPrimitiveJavaTypeName(qName)
+            if (javaPrimitive != null) {
+                return KspPrimitiveType(this, ksType)
+            }
+        }
+        return if (qName == KOTLIN_ARRAY_Q_NAME) {
+            KspArrayType(
+                env = this,
+                ksType = ksType
+            )
+        } else {
+            KspDeclaredType(this, ksType)
+        }
+    }
+
     fun wrapClassDeclaration(declaration: KSClassDeclaration): KspTypeElement {
         return KspTypeElement(
             env = this,
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
index fecc613..3b825dd 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
@@ -118,7 +118,17 @@
 
     override fun isTypeOf(other: KClass<*>): Boolean {
         // closest to what MoreTypes#isTypeOf does.
-        return rawType.typeName.toString() == other.qualifiedName
+        // TODO once we move TypeNames to java realm, we will be able to check just using that
+        //  (+ boxing). Fow now, this code stays flexible and checks for all variations as
+        //  only primitives use java types now.
+        val rawTypeName = rawType.typeName
+        // other might be something like Kotlin.Int which would map to primitive int (when
+        // invoked with .java) hence we need to check the qualified name for both.
+        // similar case for lists.
+        val javaQName = other.java.canonicalName
+        val kotlinQName = other.qualifiedName
+        val myQName = rawTypeName.toString()
+        return myQName == javaQName || myQName == kotlinQName
     }
 
     override fun isSameType(other: XType): Boolean {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index 91f4c17..699dd95 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -26,7 +26,6 @@
 import androidx.room.compiler.processing.ksp.KspAnnotated.UseSiteFilter.Companion.NO_USE_SITE
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticConstructorForJava
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
-import com.squareup.javapoet.ClassName
 import com.google.devtools.ksp.getAllSuperTypes
 import com.google.devtools.ksp.getDeclaredFunctions
 import com.google.devtools.ksp.getDeclaredProperties
@@ -37,6 +36,7 @@
 import com.google.devtools.ksp.symbol.KSPropertyDeclaration
 import com.google.devtools.ksp.symbol.Modifier
 import com.google.devtools.ksp.symbol.Origin
+import com.squareup.javapoet.ClassName
 
 internal class KspTypeElement(
     env: KspProcessingEnv,
@@ -72,7 +72,7 @@
     }
 
     override val type: KspDeclaredType by lazy {
-        env.wrap(declaration.asStarProjectedType())
+        env.wrapDeclared(declaration.asStarProjectedType())
     }
 
     override val superType: XType? by lazy {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeMapper.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeMapper.kt
new file mode 100644
index 0000000..2ffa3e5
--- /dev/null
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeMapper.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.ksp
+
+import com.squareup.javapoet.TypeName
+
+/**
+ * Maps java specific types to their kotlin counterparts.
+ * see: https://github.com/google/ksp/issues/126
+ * see: https://github.com/google/ksp/issues/125
+ *
+ * `Resolver.getClassDeclarationByName` returns the java representation of a class even when a
+ * kotlin version of the same class exists. e.g. It returns a KSClassDeclaration representing
+ * `java.lang.String` if queried with `java.lang.String`. Even though this makes sense by itself,
+ * it is inconsistent with the kotlin compiler which will resolve all instances of
+ * `java.lang.String` to `kotlin.String` (even if it is in Java source code).
+ *
+ * Until KSP provides compiler consistent behavior, this helper utility does the mapping for us.
+ *
+ * This list is built from https://kotlinlang.org/docs/reference/java-interop.html#mapped-types.
+ * Hopefully, it will be temporary until KSP provides a utility to do the same conversion.
+ */
+object KspTypeMapper {
+    private val mapping = mutableMapOf<String, String>()
+    private val kotlinTypeToJavaPrimitiveMapping = mapOf(
+        "kotlin.Byte" to TypeName.BYTE,
+        "kotlin.Short" to TypeName.SHORT,
+        "kotlin.Int" to TypeName.INT,
+        "kotlin.Long" to TypeName.LONG,
+        "kotlin.Char" to TypeName.CHAR,
+        "kotlin.Float" to TypeName.FLOAT,
+        "kotlin.Double" to TypeName.DOUBLE,
+        "kotlin.Boolean" to TypeName.BOOLEAN
+    )
+
+    init {
+        // https://kotlinlang.org/docs/reference/java-interop.html#mapped-types
+        kotlinTypeToJavaPrimitiveMapping.forEach {
+            mapping[it.value.toString()] = it.key
+        }
+        // TODO Add non primitives after TypeNames move to the java type realm.
+    }
+
+    fun swapWithKotlinType(javaType: String): String = mapping[javaType] ?: javaType
+
+    fun getPrimitiveJavaTypeName(kotlinType: String) = kotlinTypeToJavaPrimitiveMapping[kotlinType]
+
+    fun isJavaPrimitiveType(qName: String) = mapping[qName]?.let {
+        kotlinTypeToJavaPrimitiveMapping[it]
+    } != null
+}
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
index 1b5f8f4..42deaa39 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
@@ -65,7 +65,7 @@
                 )
             )
         )
-        env.wrap(contType)
+        env.wrapDeclared(contType)
     }
 
     override fun asMemberOf(other: XDeclaredType): XType {
@@ -83,7 +83,7 @@
             Variance.CONTRAVARIANT
         )
         val contType = continuation.asType(listOf(returnTypeAsTypeArgument))
-        return env.wrap(contType)
+        return env.wrapDeclared(contType)
     }
 
     override fun kindName(): String {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
index 97c42f8..a8932b4 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
@@ -179,7 +179,7 @@
         }
 
         override val returnType: XType by lazy {
-            env.wrap(
+            env.wrapDeclared(
                 env.resolver.builtIns.unitType
             )
         }
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
index bb3b019..cbce886 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
@@ -25,6 +25,7 @@
 import com.google.common.truth.Truth
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.WildcardTypeName
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -202,7 +203,7 @@
                     invocation.types.string
                 )
                 assertThat(method.parameters[1].type.typeName).isEqualTo(
-                    invocation.types.int
+                    TypeName.INT
                 )
                 assertThat(method.isSuspendFunction()).isTrue()
                 assertThat(method.returnType.typeName).isEqualTo(invocation.types.objectOrAny)
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
index 2ea0df4..a5a7bc6 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
@@ -139,13 +139,16 @@
             }
             """.trimIndent()
         )
-        runProcessorTest(
+        runProcessorTestIncludingKsp(
             listOf(source)
         ) { invocation ->
             PRIMITIVE_TYPES.forEach {
                 val targetType = invocation.processingEnv.findType(it.key)
                 assertThat(targetType?.typeName).isEqualTo(it.value)
-                assertThat(targetType?.boxed()?.typeName).isEqualTo(it.value.box())
+                if (!invocation.isKsp) {
+                    // TODO re-enable once we move typenames to the java realm
+                    assertThat(targetType?.boxed()?.typeName).isEqualTo(it.value.box())
+                }
             }
         }
     }
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
index 8a3c47f..2c297e0 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
@@ -19,9 +19,11 @@
 import com.google.auto.common.MoreElements
 import com.google.common.truth.Truth
 import com.google.common.truth.Truth.assertThat
-import com.google.testing.compile.CompileTester
 import com.google.testing.compile.JavaFileObjects
 import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -96,7 +98,7 @@
                     "field4:Ljava/util/List;"
                 )
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
@@ -153,7 +155,7 @@
                     "method9()Ljava/lang/String;"
                 )
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
@@ -179,7 +181,7 @@
         ) { descriptors ->
             assertThat(descriptors)
                 .isEqualTo(setOf("method1(ZI)V", "method2(C)B", "method3(DF)V", "method4(JS)V"))
-        }.compilesWithoutError()
+        }
     }
 
     @Test
@@ -215,7 +217,7 @@
                     "method4()Ljava/util/Map;"
                 )
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
@@ -247,7 +249,7 @@
                     "method2()Landroidx/room/test/DataClass;"
                 )
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
@@ -296,7 +298,7 @@
                     "method4()Landroidx/room/test/DataClass\$StaticInnerData;"
                 )
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
@@ -336,7 +338,72 @@
                     "method4([I)V"
                 )
             )
-        }.compilesWithoutError()
+        }
+    }
+
+    @Test
+    fun typeNameFromDescriptor() {
+        val extraJfo =
+            """
+            import androidx.room.test.Describe;
+            class Custom {
+                static class Nested1 {
+                    static class Nested2 {
+                        @Describe
+                        Custom c;
+                        @Describe
+                        Custom.Nested1 n1;
+                        @Describe
+                        Custom.Nested1.Nested2 n2;
+                    }
+                }
+            }
+            """.toJFO("Custom")
+        singleRun(
+            """
+            package androidx.room.test;
+
+            class Foo {
+                @Describe
+                int x;
+
+                @Describe
+                java.util.Map map;
+
+                @Describe
+                java.util.Map.Entry entry;
+
+                @Describe
+                int[] intArray;
+
+                @Describe
+                int[][] intArrayOfArray;
+            }
+            """.toJFO("androidx.room.test.Foo"),
+            extraJfo
+        ) {
+            assertThat(
+                it.map {
+                    // the format is name:type so we strip everything before `:`
+                    it.split(':')[1].typeNameFromJvmSignature()
+                }
+            ).containsExactly(
+                TypeName.INT,
+                ClassName.get(Map::class.java),
+                ClassName.get(Map.Entry::class.java),
+                ArrayTypeName.of(
+                    TypeName.INT
+                ),
+                ArrayTypeName.of(
+                    ArrayTypeName.of(
+                        TypeName.INT
+                    )
+                ),
+                ClassName.get("", "Custom"),
+                ClassName.get("", "Custom", "Nested1"),
+                ClassName.get("", "Custom", "Nested1", "Nested2"),
+            )
+        }
     }
 
     private fun String.toJFO(qName: String): JavaFileObject =
@@ -346,27 +413,31 @@
     private fun singleRun(
         vararg jfo: JavaFileObject,
         handler: (Set<String>) -> Unit
-    ): CompileTester {
-        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+    ) {
+        Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
             .that(listOf(describeAnnotation) + jfo)
             .processedWith(object : AbstractProcessor() {
                 override fun process(
                     annotations: Set<TypeElement>,
                     roundEnv: RoundEnvironment
                 ): Boolean {
-                    roundEnv.getElementsAnnotatedWith(annotations.first()).map { element ->
-                        when (element.kind) {
-                            FIELD -> MoreElements.asVariable(element).descriptor()
-                            METHOD, CONSTRUCTOR -> MoreElements.asExecutable(element).descriptor()
-                            else -> error("Unsupported element to describe.")
-                        }
-                    }.toSet().let(handler)
+                    if (annotations.isNotEmpty()) {
+                        roundEnv.getElementsAnnotatedWith(annotations.first()).map { element ->
+                            when (element.kind) {
+                                FIELD ->
+                                    MoreElements.asVariable(element).descriptor()
+                                METHOD, CONSTRUCTOR ->
+                                    MoreElements.asExecutable(element).descriptor()
+                                else -> error("Unsupported element to describe.")
+                            }
+                        }.toSet().let(handler)
+                    }
                     return true
                 }
 
-                override fun getSupportedOptions(): Set<String> {
+                override fun getSupportedAnnotationTypes(): Set<String> {
                     return setOf("androidx.room.test.Describe")
                 }
-            })
+            }).compilesWithoutError()
     }
 }
\ No newline at end of file
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
index 3944482..5b6e65f 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
@@ -27,7 +27,7 @@
 import com.google.devtools.ksp.getClassDeclarationByName
 import com.google.devtools.ksp.getDeclaredFunctions
 import com.google.devtools.ksp.symbol.KSPropertyDeclaration
-import com.google.devtools.ksp.symbol.KSTypeReference
+import com.squareup.javapoet.TypeName
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -88,14 +88,14 @@
             listOf(src),
             succeed = false
         ) { invocation ->
-            invocation.requirePropertyType("errorType").let { type ->
+            invocation.requireDeclaredPropertyType("errorType").let { type ->
                 assertThat(type.isError()).isTrue()
                 assertThat(type.typeArguments).isEmpty()
                 assertThat(type.typeName).isEqualTo(ERROR_TYPE_NAME)
                 assertThat(type.asTypeElement().className).isEqualTo(ERROR_TYPE_NAME)
             }
 
-            invocation.requirePropertyType("listOfErrorType").let { type ->
+            invocation.requireDeclaredPropertyType("listOfErrorType").let { type ->
                 assertThat(type.isError()).isFalse()
                 assertThat(type.typeArguments).hasSize(1)
                 type.typeArguments.single().let { typeArg ->
@@ -120,7 +120,7 @@
             listOf(src),
             succeed = true
         ) { invocation ->
-            invocation.requirePropertyType("listOfNullableStrings").let { type ->
+            invocation.requireDeclaredPropertyType("listOfNullableStrings").let { type ->
                 assertThat(type.nullability).isEqualTo(NONNULL)
                 assertThat(type.typeArguments).hasSize(1)
                 assertThat(type.asTypeElement().className).isEqualTo(
@@ -136,7 +136,7 @@
                 }
             }
 
-            invocation.requirePropertyType("listOfInts").let { type ->
+            invocation.requireDeclaredPropertyType("listOfInts").let { type ->
                 assertThat(type.nullability).isEqualTo(NONNULL)
                 assertThat(type.typeArguments).hasSize(1)
                 type.typeArguments.single().let { typeArg ->
@@ -172,13 +172,17 @@
             listOf(src),
             succeed = true
         ) { invocation ->
-            val nullableStringList = invocation.requirePropertyType("listOfNullableStrings")
-            val nonNullStringList = invocation.requirePropertyType("listOfNonNullStrings")
+            val nullableStringList = invocation
+                .requireDeclaredPropertyType("listOfNullableStrings")
+            val nonNullStringList = invocation
+                .requireDeclaredPropertyType("listOfNonNullStrings")
             assertThat(nullableStringList).isNotEqualTo(nonNullStringList)
             assertThat(nonNullStringList).isNotEqualTo(nullableStringList)
 
-            val nullableStringList_2 = invocation.requirePropertyType("listOfNullableStrings_2")
-            val nonNullStringList_2 = invocation.requirePropertyType("listOfNonNullStrings_2")
+            val nullableStringList_2 = invocation
+                .requireDeclaredPropertyType("listOfNullableStrings_2")
+            val nonNullStringList_2 = invocation
+                .requireDeclaredPropertyType("listOfNonNullStrings_2")
             assertThat(nullableStringList).isEqualTo(nullableStringList_2)
             assertThat(nonNullStringList).isEqualTo(nonNullStringList_2)
 
@@ -216,21 +220,21 @@
             succeed = true
         ) { invocation ->
             invocation.requirePropertyType("simple").let {
-                assertThat(it.rawType.typeName).isEqualTo(ClassName.get("kotlin", "Int"))
+                assertThat(it.rawType.typeName).isEqualTo(TypeName.INT)
             }
-            invocation.requirePropertyType("list").let { list ->
+            invocation.requireDeclaredPropertyType("list").let { list ->
                 assertThat(list.rawType).isNotEqualTo(list)
                 assertThat(list.typeArguments).isNotEmpty()
                 assertThat(list.rawType.typeName)
                     .isEqualTo(ClassName.get("kotlin.collections", "List"))
             }
-            invocation.requirePropertyType("map").let { map ->
+            invocation.requireDeclaredPropertyType("map").let { map ->
                 assertThat(map.rawType).isNotEqualTo(map)
                 assertThat(map.typeArguments).hasSize(2)
                 assertThat(map.rawType.typeName)
                     .isEqualTo(ClassName.get("kotlin.collections", "Map"))
             }
-            invocation.requirePropertyType("listOfMaps").let { listOfMaps ->
+            invocation.requireDeclaredPropertyType("listOfMaps").let { listOfMaps ->
                 assertThat(listOfMaps.rawType).isNotEqualTo(listOfMaps)
                 assertThat(listOfMaps.typeArguments).hasSize(1)
             }
@@ -450,7 +454,7 @@
                     .single()
                     .type
                     .let {
-                        invocation.wrap(typeRef = it!!)
+                        invocation.processingEnv.wrapDeclared(it!!.resolve())
                     }
             }
             assertThat(typeArgs["Bar"]!!.typeName)
@@ -486,7 +490,9 @@
                 ?.first {
                     it.simpleName.asString() == "wildcardMethod"
                 } ?: throw AssertionError("cannot find test method")
-            val paramType = invocation.wrap(method.parameters.first().type!!)
+            val paramType = invocation.processingEnv.wrapDeclared(
+                method.parameters.first().type!!.resolve()
+            )
             val arg1 = paramType.typeArguments.single()
             assertThat(arg1.typeName)
                 .isEqualTo(
@@ -498,17 +504,22 @@
         }
     }
 
-    private fun TestInvocation.requirePropertyType(name: String): KspDeclaredType {
-        (processingEnv as KspProcessingEnv).resolver.getAllFiles().forEach { file ->
-            val prop = file.declarations.first {
-                it.simpleName.asString() == name
-            } as KSPropertyDeclaration
-            return wrap(prop.type)
-        }
-        throw IllegalStateException("cannot find any property with name $name")
+    private fun TestInvocation.requirePropertyType(name: String): KspType {
+        val prop = requireProperty(name)
+        return (processingEnv as KspProcessingEnv).wrap(prop.type)
     }
 
-    private fun TestInvocation.wrap(typeRef: KSTypeReference): KspDeclaredType {
-        return (processingEnv as KspProcessingEnv).wrap(typeRef)
+    private fun TestInvocation.requireDeclaredPropertyType(name: String): KspDeclaredType {
+        val prop = requireProperty(name)
+        return (processingEnv as KspProcessingEnv).wrapDeclared(prop.type.resolve())
+    }
+
+    private fun TestInvocation.requireProperty(name: String): KSPropertyDeclaration {
+        kspResolver.getAllFiles().forEach { file ->
+            return file.declarations.first {
+                it.simpleName.asString() == name
+            } as KSPropertyDeclaration
+        }
+        throw IllegalStateException("cannot find any property with name $name")
     }
 }
\ No newline at end of file
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/util/TestInvocation.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/util/TestInvocation.kt
index 5e0d428..ab5c68a 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/util/TestInvocation.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/util/TestInvocation.kt
@@ -42,8 +42,6 @@
                 voidOrUnit = KotlinTypeNames.UNIT_CLASS_NAME,
                 objectOrAny = KotlinTypeNames.ANY_CLASS_NAME,
                 boxedInt = KotlinTypeNames.INT_CLASS_NAME,
-                int = KotlinTypeNames.INT_CLASS_NAME,
-                long = KotlinTypeNames.LONG_CLASS_NAME,
                 list = KotlinTypeNames.LIST_CLASS_NAME,
                 mutableSet = KotlinTypeNames.MUTABLESET_CLASS_NAME
             )
@@ -53,8 +51,6 @@
                 voidOrUnit = TypeName.VOID,
                 objectOrAny = TypeName.OBJECT,
                 boxedInt = TypeName.INT.box(),
-                int = TypeName.INT,
-                long = TypeName.LONG,
                 list = ClassName.get("java.util", "List"),
                 mutableSet = ClassName.get("java.util", "Set")
             )
@@ -70,8 +66,8 @@
         val voidOrUnit: TypeName,
         val objectOrAny: ClassName,
         val boxedInt: TypeName,
-        val int: TypeName,
-        val long: TypeName,
+        val int: TypeName = TypeName.INT,
+        val long: TypeName = TypeName.LONG,
         val list: ClassName,
         val mutableSet: TypeName
     )
diff --git a/settings.gradle b/settings.gradle
index 64e1358..719a0b6 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -214,6 +214,7 @@
 includeProject(":compose:foundation:foundation:foundation-samples", "compose/foundation/foundation/samples", [BuildType.COMPOSE])
 includeProject(":compose:integration-tests", "compose/integration-tests", [BuildType.COMPOSE])
 includeProject(":compose:integration-tests:benchmark", "compose/integration-tests/benchmark", [BuildType.COMPOSE])
+includeProject(":compose:integration-tests:macro-benchmark", "compose/integration-tests/macro-benchmark", [BuildType.COMPOSE])
 includeProject(":compose:integration-tests:demos", "compose/integration-tests/demos", [BuildType.COMPOSE])
 includeProject(":compose:integration-tests:demos:common", "compose/integration-tests/demos/common", [BuildType.COMPOSE])
 includeProject(":compose:internal-lint-checks", "compose/internal-lint-checks", [BuildType.COMPOSE])
@@ -385,7 +386,7 @@
 includeProject(":navigation:navigation-ui", "navigation/navigation-ui", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":navigation:navigation-ui-ktx", "navigation/navigation-ui-ktx", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":paging:integration-tests:testapp", "paging/integration-tests/testapp", [BuildType.MAIN])
-includeProject(":paging:paging-common", "paging/common", [BuildType.MAIN])
+includeProject(":paging:paging-common", "paging/common", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":paging:paging-common-ktx", "paging/common/ktx", [BuildType.MAIN])
 includeProject(":paging:paging-compose", "paging/compose", [BuildType.COMPOSE])
 includeProject(":paging:paging-compose:paging-compose-samples", "paging/compose/samples", [BuildType.COMPOSE])
@@ -551,14 +552,14 @@
 //
 /////////////////////////////
 
-includeProject(":internal-testutils-common", "testutils/testutils-common", [BuildType.MAIN])
+includeProject(":internal-testutils-common", "testutils/testutils-common", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":internal-testutils-runtime", "testutils/testutils-runtime", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":internal-testutils-appcompat", "testutils/testutils-appcompat", [BuildType.MAIN])
 includeProject(":internal-testutils-espresso", "testutils/testutils-espresso", [BuildType.MAIN])
 includeProject(":internal-testutils-truth", "testutils/testutils-truth", [BuildType.MAIN, BuildType.FLAN])
-includeProject(":internal-testutils-ktx", "testutils/testutils-ktx", [BuildType.MAIN])
+includeProject(":internal-testutils-ktx", "testutils/testutils-ktx", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":internal-testutils-navigation", "testutils/testutils-navigation", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN])
-includeProject(":internal-testutils-paging", "testutils/testutils-paging", [BuildType.MAIN])
+includeProject(":internal-testutils-paging", "testutils/testutils-paging", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":internal-testutils-gradle-plugin", "testutils/testutils-gradle-plugin", [BuildType.MAIN, BuildType.FLAN])
 
 /////////////////////////////
@@ -566,18 +567,27 @@
 // Applications and libraries for tests
 //
 /////////////////////////////
+includeProject(":media:version-compat-tests:client",
+        "media/version-compat-tests/current/client", [BuildType.MAIN])
+includeProject(":media:version-compat-tests:client-previous",
+        "media/version-compat-tests/previous/client", [BuildType.MAIN])
+includeProject(":media:version-compat-tests:service",
+        "media/version-compat-tests/current/service", [BuildType.MAIN])
+includeProject(":media:version-compat-tests:service-previous",
+        "media/version-compat-tests/previous/service", [BuildType.MAIN])
+includeProject(":media:version-compat-tests:lib", "media/version-compat-tests/lib",
+        [BuildType.MAIN])
 
-includeProject(":support-media-test-client", "media/version-compat-tests/current/client", [BuildType.MAIN])
-includeProject(":support-media-test-client-previous", "media/version-compat-tests/previous/client", [BuildType.MAIN])
-includeProject(":support-media-test-service", "media/version-compat-tests/current/service", [BuildType.MAIN])
-includeProject(":support-media-test-service-previous", "media/version-compat-tests/previous/service", [BuildType.MAIN])
-includeProject(":support-media-test-lib", "media/version-compat-tests/lib", [BuildType.MAIN])
-
-includeProject(":support-media2-test-client", "media2/session/version-compat-tests/current/client", [BuildType.MAIN])
-includeProject(":support-media2-test-client-previous", "media2/session/version-compat-tests/previous/client", [BuildType.MAIN])
-includeProject(":support-media2-test-service", "media2/session/version-compat-tests/current/service", [BuildType.MAIN])
-includeProject(":support-media2-test-service-previous", "media2/session/version-compat-tests/previous/service", [BuildType.MAIN])
-includeProject(":support-media2-test-common", "media2/session/version-compat-tests/common", [BuildType.MAIN])
+includeProject(":media2:media2-session:version-compat-tests:client",
+        "media2/session/version-compat-tests/current/client", [BuildType.MAIN])
+includeProject(":media2:media2-session:version-compat-tests:client-previous",
+        "media2/session/version-compat-tests/previous/client", [BuildType.MAIN])
+includeProject(":media2:media2-session:version-compat-tests:service",
+        "media2/session/version-compat-tests/current/service", [BuildType.MAIN])
+includeProject(":media2:media2-session:version-compat-tests:service-previous",
+        "media2/session/version-compat-tests/previous/service", [BuildType.MAIN])
+includeProject(":media2:media2-session:version-compat-tests:common",
+        "media2/session/version-compat-tests/common", [BuildType.MAIN])
 
 /////////////////////////////
 //
diff --git a/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingSource.kt b/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingSource.kt
index e0216e4..d0113b5 100644
--- a/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingSource.kt
+++ b/testutils/testutils-paging/src/main/java/androidx/paging/TestPagingSource.kt
@@ -29,11 +29,14 @@
     counted: Boolean = true,
     override val jumpingSupported: Boolean = true,
     val items: List<Int> = ITEMS,
-    private val loadDelay: Long = 1000
+    private val loadDelay: Long = 1000,
 ) : PagingSource<Int, Int>() {
     var errorNextLoad = false
     var nextLoadResult: LoadResult<Int, Int>? = null
 
+    val getRefreshKeyCalls = mutableListOf<PagingState<Int, Int>>()
+    val loadedPages = mutableListOf<LoadResult.Page<Int, Int>>()
+
     init {
         if (!counted) {
             throw NotImplementedError(
@@ -74,11 +77,14 @@
             if (end < items.size) end else null,
             start,
             items.size - end
-        )
+        ).also {
+            loadedPages.add(it)
+        }
     }
 
     @OptIn(ExperimentalPagingApi::class)
     override fun getRefreshKey(state: PagingState<Int, Int>): Int? {
+        getRefreshKeyCalls.add(state)
         return state.anchorPosition
     }
 
diff --git a/wear/wear-complications-data/api/restricted_current.txt b/wear/wear-complications-data/api/restricted_current.txt
index 304c31b..83e4f49 100644
--- a/wear/wear-complications-data/api/restricted_current.txt
+++ b/wear/wear-complications-data/api/restricted_current.txt
@@ -167,11 +167,11 @@
   }
 
   public final class DefaultComplicationProviderPolicy {
-    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public DefaultComplicationProviderPolicy(java.util.List<android.content.ComponentName> providers, int systemProviderFallback);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public DefaultComplicationProviderPolicy(java.util.List<android.content.ComponentName> providers, @androidx.wear.complications.SystemProviders.ProviderId int systemProviderFallback);
     ctor public DefaultComplicationProviderPolicy();
-    ctor public DefaultComplicationProviderPolicy(int systemProvider);
-    ctor public DefaultComplicationProviderPolicy(android.content.ComponentName provider, int systemProviderFallback);
-    ctor public DefaultComplicationProviderPolicy(android.content.ComponentName primaryProvider, android.content.ComponentName secondaryProvider, int systemProviderFallback);
+    ctor public DefaultComplicationProviderPolicy(@androidx.wear.complications.SystemProviders.ProviderId int systemProvider);
+    ctor public DefaultComplicationProviderPolicy(android.content.ComponentName provider, @androidx.wear.complications.SystemProviders.ProviderId int systemProviderFallback);
+    ctor public DefaultComplicationProviderPolicy(android.content.ComponentName primaryProvider, android.content.ComponentName secondaryProvider, @androidx.wear.complications.SystemProviders.ProviderId int systemProviderFallback);
     method public android.content.ComponentName? getPrimaryProvider();
     method public android.content.ComponentName? getSecondaryProvider();
     method public int getSystemProviderFallback();
diff --git a/wear/wear-complications-data/src/main/java/androidx/wear/complications/DefaultComplicationProviderPolicy.kt b/wear/wear-complications-data/src/main/java/androidx/wear/complications/DefaultComplicationProviderPolicy.kt
index d053729..81e61f9 100644
--- a/wear/wear-complications-data/src/main/java/androidx/wear/complications/DefaultComplicationProviderPolicy.kt
+++ b/wear/wear-complications-data/src/main/java/androidx/wear/complications/DefaultComplicationProviderPolicy.kt
@@ -44,7 +44,7 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public constructor(
         providers: List<ComponentName>,
-        systemProviderFallback: Int
+        @SystemProviders.ProviderId systemProviderFallback: Int
     ) {
         this.primaryProvider = if (providers.isNotEmpty()) providers[0] else null
         this.secondaryProvider = if (providers.size >= 2) providers[1] else null
@@ -61,7 +61,7 @@
     /**
      * Uses systemProvider as the default complication provider.
      */
-    public constructor(systemProvider: Int) {
+    public constructor(@SystemProviders.ProviderId systemProvider: Int) {
         primaryProvider = null
         secondaryProvider = null
         systemProviderFallback = systemProvider
@@ -71,7 +71,10 @@
      * Attempts to use provider as the default complication provider, if not present then
      * systemProviderFallback will be used instead.
      */
-    public constructor(provider: ComponentName, systemProviderFallback: Int) {
+    public constructor(
+        provider: ComponentName,
+        @SystemProviders.ProviderId systemProviderFallback: Int
+    ) {
         primaryProvider = provider
         secondaryProvider = null
         this.systemProviderFallback = systemProviderFallback
@@ -85,7 +88,7 @@
     public constructor(
         primaryProvider: ComponentName,
         secondaryProvider: ComponentName,
-        systemProviderFallback: Int
+        @SystemProviders.ProviderId systemProviderFallback: Int
     ) {
         this.primaryProvider = primaryProvider
         this.secondaryProvider = secondaryProvider
diff --git a/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/AsynchronousProviderService.kt b/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/AsynchronousProviderService.kt
index cf5a586..59dc0de 100644
--- a/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/AsynchronousProviderService.kt
+++ b/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/AsynchronousProviderService.kt
@@ -35,12 +35,12 @@
         executor.execute {
             callback.onUpdateComplication(
                 when (type) {
-                    ShortTextComplicationData.TYPE ->
+                    ComplicationType.SHORT_TEXT ->
                         ShortTextComplicationData.Builder(
                             ComplicationText.plain("# $complicationId")
                         ).build()
 
-                    LongTextComplicationData.TYPE ->
+                    ComplicationType.LONG_TEXT ->
                         LongTextComplicationData.Builder(
                             ComplicationText.plain("hello $complicationId")
                         ).build()
@@ -52,11 +52,11 @@
     }
 
     override fun getPreviewData(type: ComplicationType) = when (type) {
-        ShortTextComplicationData.TYPE ->
+        ComplicationType.SHORT_TEXT ->
             ShortTextComplicationData.Builder(ComplicationText.plain("# 123"))
                 .build()
 
-        LongTextComplicationData.TYPE ->
+        ComplicationType.LONG_TEXT ->
             LongTextComplicationData.Builder(ComplicationText.plain("hello 123"))
                 .build()
 
diff --git a/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/BackgroundProviderService.kt b/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/BackgroundProviderService.kt
index 85caa05..a4486e1 100644
--- a/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/BackgroundProviderService.kt
+++ b/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/BackgroundProviderService.kt
@@ -63,12 +63,12 @@
     ) {
         callback.onUpdateComplication(
             when (type) {
-                ShortTextComplicationData.TYPE ->
+                ComplicationType.SHORT_TEXT ->
                     ShortTextComplicationData.Builder(
                         ComplicationText.plain("# $counter")
                     ).build()
 
-                LongTextComplicationData.TYPE ->
+                ComplicationType.LONG_TEXT ->
                     LongTextComplicationData.Builder(
                         ComplicationText.plain("Count $counter")
                     ).build()
@@ -79,12 +79,12 @@
     }
 
     override fun getPreviewData(type: ComplicationType) = when (type) {
-        ShortTextComplicationData.TYPE ->
+        ComplicationType.SHORT_TEXT ->
             ShortTextComplicationData.Builder(
                 ComplicationText.plain("# 123")
             ).build()
 
-        LongTextComplicationData.TYPE ->
+        ComplicationType.LONG_TEXT ->
             LongTextComplicationData.Builder(
                 ComplicationText.plain("Count 123")
             ).build()
diff --git a/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/SynchronousProviderService.kt b/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/SynchronousProviderService.kt
index 6138270..4b9e243 100644
--- a/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/SynchronousProviderService.kt
+++ b/wear/wear-complications-provider/samples/src/main/java/androidx/wear/complications/samples/SynchronousProviderService.kt
@@ -32,12 +32,12 @@
     ) {
         callback.onUpdateComplication(
             when (type) {
-                ShortTextComplicationData.TYPE ->
+                ComplicationType.SHORT_TEXT ->
                     ShortTextComplicationData.Builder(
                         ComplicationText.plain("# $complicationId")
                     ).build()
 
-                LongTextComplicationData.TYPE ->
+                ComplicationType.LONG_TEXT ->
                     LongTextComplicationData.Builder(
                         ComplicationText.plain("hello $complicationId")
                     ).build()
@@ -48,11 +48,11 @@
     }
 
     override fun getPreviewData(type: ComplicationType) = when (type) {
-        ShortTextComplicationData.TYPE ->
+        ComplicationType.SHORT_TEXT ->
             ShortTextComplicationData.Builder(ComplicationText.plain("# 123"))
                 .build()
 
-        LongTextComplicationData.TYPE ->
+        ComplicationType.LONG_TEXT ->
             LongTextComplicationData.Builder(ComplicationText.plain("hello 123"))
                 .build()
 
diff --git a/wear/wear-watchface-client/api/current.txt b/wear/wear-watchface-client/api/current.txt
index 97c2972..4e593ce 100644
--- a/wear/wear-watchface-client/api/current.txt
+++ b/wear/wear-watchface-client/api/current.txt
@@ -113,12 +113,16 @@
     property public final int interruptionFilter;
   }
 
-  public final class WatchFaceControlClient implements java.lang.AutoCloseable {
-    ctor public WatchFaceControlClient(android.content.Context context, String watchFacePackageName);
-    method public void close();
+  public interface WatchFaceControlClient extends java.lang.AutoCloseable {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.HeadlessWatchFaceClient> createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, int surfaceWidth, int surfaceHeight);
-    method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.InteractiveWatchFaceSysUiClient> getInteractiveWatchFaceInstanceSysUi(String instanceId);
+    method public default static androidx.wear.watchface.client.WatchFaceControlClient createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.InteractiveWatchFaceSysUiClient> getInteractiveWatchFaceSysUiClientInstance(String instanceId);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.InteractiveWatchFaceWcsClient> getOrCreateWallpaperServiceBackedInteractiveWatchFaceWcsClient(String id, androidx.wear.watchface.client.DeviceConfig deviceConfig, androidx.wear.watchface.client.SystemState systemState, androidx.wear.watchface.style.UserStyle? userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData>? idToComplicationData);
+    field public static final androidx.wear.watchface.client.WatchFaceControlClient.Companion Companion;
+  }
+
+  public static final class WatchFaceControlClient.Companion {
+    method public androidx.wear.watchface.client.WatchFaceControlClient createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
   }
 
   public static final class WatchFaceControlClient.ServiceNotBoundException extends java.lang.Exception {
diff --git a/wear/wear-watchface-client/api/public_plus_experimental_current.txt b/wear/wear-watchface-client/api/public_plus_experimental_current.txt
index a37e9cb..8b42a9b 100644
--- a/wear/wear-watchface-client/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface-client/api/public_plus_experimental_current.txt
@@ -113,12 +113,16 @@
     property public final int interruptionFilter;
   }
 
-  public final class WatchFaceControlClient implements java.lang.AutoCloseable {
-    ctor public WatchFaceControlClient(android.content.Context context, String watchFacePackageName);
-    method public void close();
+  public interface WatchFaceControlClient extends java.lang.AutoCloseable {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.HeadlessWatchFaceClient> createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, int surfaceWidth, int surfaceHeight);
-    method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.InteractiveWatchFaceSysUiClient> getInteractiveWatchFaceInstanceSysUi(String instanceId);
+    method public default static androidx.wear.watchface.client.WatchFaceControlClient createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.InteractiveWatchFaceSysUiClient> getInteractiveWatchFaceSysUiClientInstance(String instanceId);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.InteractiveWatchFaceWcsClient> getOrCreateWallpaperServiceBackedInteractiveWatchFaceWcsClient(String id, androidx.wear.watchface.client.DeviceConfig deviceConfig, androidx.wear.watchface.client.SystemState systemState, androidx.wear.watchface.style.UserStyle? userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData>? idToComplicationData);
+    field public static final androidx.wear.watchface.client.WatchFaceControlClient.Companion Companion;
+  }
+
+  public static final class WatchFaceControlClient.Companion {
+    method public androidx.wear.watchface.client.WatchFaceControlClient createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
   }
 
   public static final class WatchFaceControlClient.ServiceNotBoundException extends java.lang.Exception {
diff --git a/wear/wear-watchface-client/api/restricted_current.txt b/wear/wear-watchface-client/api/restricted_current.txt
index de254fe..62a3fbc 100644
--- a/wear/wear-watchface-client/api/restricted_current.txt
+++ b/wear/wear-watchface-client/api/restricted_current.txt
@@ -116,12 +116,16 @@
   @IntDef({androidx.wear.watchface.client.InteractiveWatchFaceSysUiClient.TAP_TYPE_TOUCH, androidx.wear.watchface.client.InteractiveWatchFaceSysUiClient.TAP_TYPE_TOUCH_CANCEL, androidx.wear.watchface.client.InteractiveWatchFaceSysUiClient.TAP_TYPE_TAP}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public @interface TapType {
   }
 
-  public final class WatchFaceControlClient implements java.lang.AutoCloseable {
-    ctor public WatchFaceControlClient(android.content.Context context, String watchFacePackageName);
-    method public void close();
+  public interface WatchFaceControlClient extends java.lang.AutoCloseable {
     method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.HeadlessWatchFaceClient> createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, int surfaceWidth, int surfaceHeight);
-    method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.InteractiveWatchFaceSysUiClient> getInteractiveWatchFaceInstanceSysUi(String instanceId);
+    method public default static androidx.wear.watchface.client.WatchFaceControlClient createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.InteractiveWatchFaceSysUiClient> getInteractiveWatchFaceSysUiClientInstance(String instanceId);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.InteractiveWatchFaceWcsClient> getOrCreateWallpaperServiceBackedInteractiveWatchFaceWcsClient(String id, androidx.wear.watchface.client.DeviceConfig deviceConfig, androidx.wear.watchface.client.SystemState systemState, androidx.wear.watchface.style.UserStyle? userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.complications.data.ComplicationData>? idToComplicationData);
+    field public static final androidx.wear.watchface.client.WatchFaceControlClient.Companion Companion;
+  }
+
+  public static final class WatchFaceControlClient.Companion {
+    method public androidx.wear.watchface.client.WatchFaceControlClient createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
   }
 
   public static final class WatchFaceControlClient.ServiceNotBoundException extends java.lang.Exception {
diff --git a/wear/wear-watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt b/wear/wear-watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
index 264a729..a2304a8 100644
--- a/wear/wear-watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
+++ b/wear/wear-watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
@@ -37,7 +37,7 @@
 import androidx.wear.watchface.RenderParameters
 import androidx.wear.watchface.client.DeviceConfig
 import androidx.wear.watchface.client.SystemState
-import androidx.wear.watchface.client.WatchFaceControlClient
+import androidx.wear.watchface.client.WatchFaceControlClientImpl
 import androidx.wear.watchface.control.WatchFaceControlService
 import androidx.wear.watchface.data.ComplicationBoundsType
 import androidx.wear.watchface.samples.EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID
@@ -62,7 +62,7 @@
 @MediumTest
 class WatchFaceControlClientTest {
     private val context = ApplicationProvider.getApplicationContext<Context>()
-    private val service = WatchFaceControlClient(
+    private val service = WatchFaceControlClientImpl(
         context,
         Intent(context, WatchFaceControlTestService::class.java).apply {
             action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
@@ -350,7 +350,7 @@
         // Wait for the instance to be created.
         interactiveInstanceFuture.get(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)!!
 
-        val sysUiInterface = service.getInteractiveWatchFaceInstanceSysUi("testId")
+        val sysUiInterface = service.getInteractiveWatchFaceSysUiClientInstance("testId")
             .get(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)!!
 
         try {
diff --git a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
index eda8a77..c2c798f 100644
--- a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
+++ b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
@@ -37,40 +37,22 @@
  * Connects to a watch face's WatchFaceControlService which allows the user to control the
  * watch face.
  */
-public class WatchFaceControlClient internal constructor(
-    private val context: Context,
-    serviceIntent: Intent
-) : AutoCloseable {
+public interface WatchFaceControlClient : AutoCloseable {
 
-    /** Constructs a client which connects to a watch face in the given android package. */
-    public constructor(
-        /** Calling application's [Context]. */
-        context: Context,
-        /** The name of the package containing the watch face control service to bind to. */
-        watchFacePackageName: String
-    ) : this(
-        context,
-        Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
-            this.setPackage(watchFacePackageName)
-        }
-    )
-
-    internal var serviceFuture = ResolvableFuture.create<IWatchFaceControlService?>()
-
-    private val serviceConnection = object : ServiceConnection {
-        override fun onServiceConnected(name: ComponentName, binder: IBinder) {
-            serviceFuture.set(IWatchFaceControlService.Stub.asInterface(binder))
-        }
-
-        override fun onServiceDisconnected(name: ComponentName) {
-            serviceFuture.set(null)
-        }
-    }
-
-    init {
-        if (!context.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE)) {
-            serviceFuture.set(null)
-        }
+    public companion object {
+        /** Constructs a client which connects to a watch face in the given android package. */
+        @JvmStatic
+        public fun createWatchFaceControlClient(
+            /** Calling application's [Context]. */
+            context: Context,
+            /** The name of the package containing the watch face control service to bind to. */
+            watchFacePackageName: String
+        ): WatchFaceControlClient = WatchFaceControlClientImpl(
+            context,
+            Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
+                this.setPackage(watchFacePackageName)
+            }
+        )
     }
 
     /**
@@ -89,27 +71,9 @@
      *    [instanceId] is unrecognized, or [ServiceNotBoundException] if the
      *    WatchFaceControlService is not bound.
      */
-    public fun getInteractiveWatchFaceInstanceSysUi(
+    public fun getInteractiveWatchFaceSysUiClientInstance(
         instanceId: String
-    ): ListenableFuture<InteractiveWatchFaceSysUiClient?> {
-        val resultFuture = ResolvableFuture.create<InteractiveWatchFaceSysUiClient>()
-        serviceFuture.addListener(
-            {
-                val service = serviceFuture.get()
-                if (service == null) {
-                    resultFuture.setException(ServiceNotBoundException())
-                } else {
-                    resultFuture.set(
-                        InteractiveWatchFaceSysUiClientImpl(
-                            service.getInteractiveWatchFaceInstanceSysUI(instanceId)
-                        )
-                    )
-                }
-            },
-            { runnable -> runnable.run() }
-        )
-        return resultFuture
-    }
+    ): ListenableFuture<InteractiveWatchFaceSysUiClient?>
 
     /**
      * Creates a [HeadlessWatchFaceClient] with the specified [DeviceConfig]. Screenshots made with
@@ -133,13 +97,88 @@
         deviceConfig: DeviceConfig,
         surfaceWidth: Int,
         surfaceHeight: Int
+    ): ListenableFuture<HeadlessWatchFaceClient?>
+
+    /**
+     * Requests either an existing [InteractiveWatchFaceWcsClient] with the specified [id] or
+     * schedules creation of an [InteractiveWatchFaceWcsClient] for the next time the
+     * WallpaperService creates an engine.
+     *
+     * NOTE that currently only one [InteractiveWatchFaceWcsClient] per process can exist at a time.
+     *
+     * @param id The ID for the requested [InteractiveWatchFaceWcsClient].
+     * @param deviceConfig The [DeviceConfig] for the wearable.
+     * @param systemState The initial [SystemState] for the wearable.
+     * @param userStyle The initial [UserStyle], or null if the default should be used.
+     * @param idToComplicationData The initial complication data, or null if unavailable.
+     * @return a [ListenableFuture] for a [InteractiveWatchFaceWcsClient]
+     */
+    public fun getOrCreateWallpaperServiceBackedInteractiveWatchFaceWcsClient(
+        id: String,
+        deviceConfig: DeviceConfig,
+        systemState: SystemState,
+        userStyle: UserStyle?,
+        idToComplicationData: Map<Int, ComplicationData>?
+    ): ListenableFuture<InteractiveWatchFaceWcsClient>
+}
+
+internal class WatchFaceControlClientImpl internal constructor(
+    private val context: Context,
+    serviceIntent: Intent
+) : WatchFaceControlClient {
+
+    internal var serviceFuture = ResolvableFuture.create<IWatchFaceControlService?>()
+
+    private val serviceConnection = object : ServiceConnection {
+        override fun onServiceConnected(name: ComponentName, binder: IBinder) {
+            serviceFuture.set(IWatchFaceControlService.Stub.asInterface(binder))
+        }
+
+        override fun onServiceDisconnected(name: ComponentName) {
+            serviceFuture.set(null)
+        }
+    }
+
+    init {
+        if (!context.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE)) {
+            serviceFuture.set(null)
+        }
+    }
+
+    override fun getInteractiveWatchFaceSysUiClientInstance(
+        instanceId: String
+    ): ListenableFuture<InteractiveWatchFaceSysUiClient?> {
+        val resultFuture = ResolvableFuture.create<InteractiveWatchFaceSysUiClient>()
+        serviceFuture.addListener(
+            {
+                val service = serviceFuture.get()
+                if (service == null) {
+                    resultFuture.setException(WatchFaceControlClient.ServiceNotBoundException())
+                } else {
+                    resultFuture.set(
+                        InteractiveWatchFaceSysUiClientImpl(
+                            service.getInteractiveWatchFaceInstanceSysUI(instanceId)
+                        )
+                    )
+                }
+            },
+            { runnable -> runnable.run() }
+        )
+        return resultFuture
+    }
+
+    override fun createHeadlessWatchFaceClient(
+        watchFaceName: ComponentName,
+        deviceConfig: DeviceConfig,
+        surfaceWidth: Int,
+        surfaceHeight: Int
     ): ListenableFuture<HeadlessWatchFaceClient?> {
         val resultFuture = ResolvableFuture.create<HeadlessWatchFaceClient?>()
         serviceFuture.addListener(
             {
                 val service = serviceFuture.get()
                 if (service == null) {
-                    resultFuture.setException(ServiceNotBoundException())
+                    resultFuture.setException(WatchFaceControlClient.ServiceNotBoundException())
                 } else {
                     resultFuture.set(
                         HeadlessWatchFaceClientImpl(
@@ -166,21 +205,7 @@
         return resultFuture
     }
 
-    /**
-     * Requests either an existing [InteractiveWatchFaceWcsClient] with the specified [id] or
-     * schedules creation of an [InteractiveWatchFaceWcsClient] for the next time the
-     * WallpaperService creates an engine.
-     *
-     * NOTE that currently only one [InteractiveWatchFaceWcsClient] per process can exist at a time.
-     *
-     * @param id The ID for the requested [InteractiveWatchFaceWcsClient].
-     * @param deviceConfig The [DeviceConfig] for the wearable.
-     * @param systemState The initial [SystemState] for the wearable.
-     * @param userStyle The initial [UserStyle], or null if the default should be used.
-     * @param idToComplicationData The initial complication data, or null if unavailable.
-     * @return a [ListenableFuture] for a [InteractiveWatchFaceWcsClient]
-     */
-    public fun getOrCreateWallpaperServiceBackedInteractiveWatchFaceWcsClient(
+    override fun getOrCreateWallpaperServiceBackedInteractiveWatchFaceWcsClient(
         id: String,
         deviceConfig: DeviceConfig,
         systemState: SystemState,
@@ -192,7 +217,7 @@
             {
                 val service = serviceFuture.get()
                 if (service == null) {
-                    resultFuture.setException(ServiceNotBoundException())
+                    resultFuture.setException(WatchFaceControlClient.ServiceNotBoundException())
                 } else {
                     val existingInstance = service.getOrCreateInteractiveWatchFaceWCS(
                         WallpaperInteractiveWatchFaceInstanceParams(
@@ -239,10 +264,6 @@
         return resultFuture
     }
 
-    /**
-     * Releases the controls service.  It is an error to issue any further commands on this
-     * interface.
-     */
     override fun close() {
         context.unbindService(serviceConnection)
         serviceFuture.set(null)
diff --git a/wear/wear-watchface-style/api/current.txt b/wear/wear-watchface-style/api/current.txt
index 1c063e1..4f45dac 100644
--- a/wear/wear-watchface-style/api/current.txt
+++ b/wear/wear-watchface-style/api/current.txt
@@ -69,6 +69,7 @@
   }
 
   public static final class UserStyleSetting.ComplicationsUserStyleSetting.ComplicationOverlay {
+    ctor public UserStyleSetting.ComplicationsUserStyleSetting.ComplicationOverlay(int complicationId, Boolean? enabled, android.graphics.RectF? bounds, java.util.List<? extends androidx.wear.complications.data.ComplicationType>? supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy? defaultProviderPolicy, androidx.wear.complications.data.ComplicationType? defaultProviderType);
     method public android.graphics.RectF? getBounds();
     method public int getComplicationId();
     method public androidx.wear.complications.DefaultComplicationProviderPolicy? getDefaultProviderPolicy();
diff --git a/wear/wear-watchface-style/api/public_plus_experimental_current.txt b/wear/wear-watchface-style/api/public_plus_experimental_current.txt
index 677a590..c223ee5 100644
--- a/wear/wear-watchface-style/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface-style/api/public_plus_experimental_current.txt
@@ -72,6 +72,7 @@
   }
 
   public static final class UserStyleSetting.ComplicationsUserStyleSetting.ComplicationOverlay {
+    ctor public UserStyleSetting.ComplicationsUserStyleSetting.ComplicationOverlay(int complicationId, Boolean? enabled, android.graphics.RectF? bounds, java.util.List<? extends androidx.wear.complications.data.ComplicationType>? supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy? defaultProviderPolicy, androidx.wear.complications.data.ComplicationType? defaultProviderType);
     method public android.graphics.RectF? getBounds();
     method public int getComplicationId();
     method public androidx.wear.complications.DefaultComplicationProviderPolicy? getDefaultProviderPolicy();
diff --git a/wear/wear-watchface-style/api/restricted_current.txt b/wear/wear-watchface-style/api/restricted_current.txt
index 41ff9a5..d43ffd7 100644
--- a/wear/wear-watchface-style/api/restricted_current.txt
+++ b/wear/wear-watchface-style/api/restricted_current.txt
@@ -78,6 +78,7 @@
   }
 
   public static final class UserStyleSetting.ComplicationsUserStyleSetting.ComplicationOverlay {
+    ctor public UserStyleSetting.ComplicationsUserStyleSetting.ComplicationOverlay(int complicationId, Boolean? enabled, android.graphics.RectF? bounds, java.util.List<? extends androidx.wear.complications.data.ComplicationType>? supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy? defaultProviderPolicy, androidx.wear.complications.data.ComplicationType? defaultProviderType);
     method public android.graphics.RectF? getBounds();
     method public int getComplicationId();
     method public androidx.wear.complications.DefaultComplicationProviderPolicy? getDefaultProviderPolicy();
diff --git a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
index 43451a91..7037d18 100644
--- a/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
+++ b/wear/wear-watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
@@ -278,7 +278,7 @@
          * Overrides to be applied to the corresponding complication's initial config (as specified
          * in [androidx.wear.watchface.Complication]) when the setting is selected.
          */
-        public class ComplicationOverlay internal constructor(
+        public class ComplicationOverlay constructor(
             /** The id of the complication to configure. */
             public val complicationId: Int,
 
@@ -287,31 +287,31 @@
              * null then no changes are made.
              */
             @get:JvmName("isEnabled")
-            public val enabled: Boolean?,
+            public val enabled: Boolean? = null,
 
             /**
              * If non null, the new unit square screen space complication bounds for this
              * configuration. If null then no changes are made.
              */
-            public val bounds: RectF?,
+            public val bounds: RectF? = null,
 
             /**
              * If non null, the new types of complication supported by this complication for this
              * configuration. If null then no changes are made.
              */
-            public val supportedTypes: List<ComplicationType>?,
+            public val supportedTypes: List<ComplicationType>? = null,
 
             /**
              * If non null, the new default complication provider for this configuration. If null
              * then no changes are made.
              */
-            public val defaultProviderPolicy: DefaultComplicationProviderPolicy?,
+            public val defaultProviderPolicy: DefaultComplicationProviderPolicy? = null,
 
             /**
              * If non null, the new default complication provider data type. If null then no changes
              * are made.
              */
-            public val defaultProviderType: ComplicationType?
+            public val defaultProviderType: ComplicationType? = null
         ) {
             public class Builder(
                 /** The id of the complication to configure. */
diff --git a/wear/wear-watchface/api/current.txt b/wear/wear-watchface/api/current.txt
index 406bd24..89192c1 100644
--- a/wear/wear-watchface/api/current.txt
+++ b/wear/wear-watchface/api/current.txt
@@ -78,17 +78,17 @@
 
   public final class ComplicationsManager {
     ctor public ComplicationsManager(java.util.Collection<androidx.wear.watchface.Complication> complicationCollection, androidx.wear.watchface.style.UserStyleRepository userStyleRepository);
-    method @UiThread public void addTapListener(androidx.wear.watchface.ComplicationsManager.TapListener tapListener);
+    method @UiThread public void addTapListener(androidx.wear.watchface.ComplicationsManager.TapCallback tapCallback);
     method @UiThread public void bringAttentionToComplication(int complicationId);
     method public operator androidx.wear.watchface.Complication? get(int id);
     method public androidx.wear.watchface.Complication? getBackgroundComplication();
     method public androidx.wear.watchface.Complication? getComplicationAt(int x, int y);
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.Complication> getComplications();
-    method @UiThread public void removeTapListener(androidx.wear.watchface.ComplicationsManager.TapListener tapListener);
+    method @UiThread public void removeTapListener(androidx.wear.watchface.ComplicationsManager.TapCallback tapCallback);
     property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.Complication> complications;
   }
 
-  public static interface ComplicationsManager.TapListener {
+  public static interface ComplicationsManager.TapCallback {
     method public default void onComplicationDoubleTapped(int complicationId);
     method public default void onComplicationSingleTapped(int complicationId);
   }
@@ -186,18 +186,38 @@
   }
 
   public final class WatchFace {
+    method public androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle getLegacyWatchFaceStyle();
+    method public long getPreviewReferenceTimeMillis();
     method @UiThread public void invalidate();
+    method public static boolean isLegacyWatchFaceOverlayStyleSupported();
     method public void postInvalidate();
+    property public final androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle legacyWatchFaceStyle;
+    property public final long previewReferenceTimeMillis;
+    field public static final androidx.wear.watchface.WatchFace.Companion Companion;
   }
 
   public static final class WatchFace.Builder {
     ctor public WatchFace.Builder(int watchFaceType, long interactiveUpdateRateMillis, internal androidx.wear.watchface.style.UserStyleRepository userStyleRepository, internal androidx.wear.watchface.ComplicationsManager complicationsManager, internal androidx.wear.watchface.Renderer renderer, androidx.wear.watchface.WatchFaceHost watchFaceHost, androidx.wear.watchface.WatchState watchState);
     method public androidx.wear.watchface.WatchFace build();
+    method public androidx.wear.watchface.WatchFace.Builder setLegacyWatchFaceStyle(androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle legacyWatchFaceStyle);
     method public androidx.wear.watchface.WatchFace.Builder setPreviewReferenceTimeMillis(long previewReferenceTimeMillis);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2AccentColor(@ColorInt int accentColor);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2AcceptsTapEvents(boolean acceptsTapEvents);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2StatusBarGravity(int statusBarGravity);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2ViewProtectionMode(int viewProtectionMode);
+  }
+
+  public static final class WatchFace.Companion {
+    method public boolean isLegacyWatchFaceOverlayStyleSupported();
+  }
+
+  public static final class WatchFace.LegacyWatchFaceOverlayStyle {
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, @ColorInt int accentColor);
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted);
+    method public int getAccentColor();
+    method public int getStatusBarGravity();
+    method public int getViewProtectionMode();
+    method public boolean isTapEventsAccepted();
+    property public final int accentColor;
+    property public final int statusBarGravity;
+    property public final boolean tapEventsAccepted;
+    property public final int viewProtectionMode;
   }
 
   public final class WatchFaceHost {
@@ -220,10 +240,10 @@
     ctor public WatchState(androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis);
     method public long getAnalogPreviewReferenceTimeMillis();
     method public long getDigitalPreviewReferenceTimeMillis();
-    method public boolean getHasBurnInProtection();
-    method public boolean getHasLowBitAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Integer> getInterruptionFilter();
     method public int getScreenShape();
+    method public boolean hasBurnInProtection();
+    method public boolean hasLowBitAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible();
     property public final long analogPreviewReferenceTimeMillis;
diff --git a/wear/wear-watchface/api/public_plus_experimental_current.txt b/wear/wear-watchface/api/public_plus_experimental_current.txt
index 406bd24..89192c1 100644
--- a/wear/wear-watchface/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface/api/public_plus_experimental_current.txt
@@ -78,17 +78,17 @@
 
   public final class ComplicationsManager {
     ctor public ComplicationsManager(java.util.Collection<androidx.wear.watchface.Complication> complicationCollection, androidx.wear.watchface.style.UserStyleRepository userStyleRepository);
-    method @UiThread public void addTapListener(androidx.wear.watchface.ComplicationsManager.TapListener tapListener);
+    method @UiThread public void addTapListener(androidx.wear.watchface.ComplicationsManager.TapCallback tapCallback);
     method @UiThread public void bringAttentionToComplication(int complicationId);
     method public operator androidx.wear.watchface.Complication? get(int id);
     method public androidx.wear.watchface.Complication? getBackgroundComplication();
     method public androidx.wear.watchface.Complication? getComplicationAt(int x, int y);
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.Complication> getComplications();
-    method @UiThread public void removeTapListener(androidx.wear.watchface.ComplicationsManager.TapListener tapListener);
+    method @UiThread public void removeTapListener(androidx.wear.watchface.ComplicationsManager.TapCallback tapCallback);
     property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.Complication> complications;
   }
 
-  public static interface ComplicationsManager.TapListener {
+  public static interface ComplicationsManager.TapCallback {
     method public default void onComplicationDoubleTapped(int complicationId);
     method public default void onComplicationSingleTapped(int complicationId);
   }
@@ -186,18 +186,38 @@
   }
 
   public final class WatchFace {
+    method public androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle getLegacyWatchFaceStyle();
+    method public long getPreviewReferenceTimeMillis();
     method @UiThread public void invalidate();
+    method public static boolean isLegacyWatchFaceOverlayStyleSupported();
     method public void postInvalidate();
+    property public final androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle legacyWatchFaceStyle;
+    property public final long previewReferenceTimeMillis;
+    field public static final androidx.wear.watchface.WatchFace.Companion Companion;
   }
 
   public static final class WatchFace.Builder {
     ctor public WatchFace.Builder(int watchFaceType, long interactiveUpdateRateMillis, internal androidx.wear.watchface.style.UserStyleRepository userStyleRepository, internal androidx.wear.watchface.ComplicationsManager complicationsManager, internal androidx.wear.watchface.Renderer renderer, androidx.wear.watchface.WatchFaceHost watchFaceHost, androidx.wear.watchface.WatchState watchState);
     method public androidx.wear.watchface.WatchFace build();
+    method public androidx.wear.watchface.WatchFace.Builder setLegacyWatchFaceStyle(androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle legacyWatchFaceStyle);
     method public androidx.wear.watchface.WatchFace.Builder setPreviewReferenceTimeMillis(long previewReferenceTimeMillis);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2AccentColor(@ColorInt int accentColor);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2AcceptsTapEvents(boolean acceptsTapEvents);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2StatusBarGravity(int statusBarGravity);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2ViewProtectionMode(int viewProtectionMode);
+  }
+
+  public static final class WatchFace.Companion {
+    method public boolean isLegacyWatchFaceOverlayStyleSupported();
+  }
+
+  public static final class WatchFace.LegacyWatchFaceOverlayStyle {
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, @ColorInt int accentColor);
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted);
+    method public int getAccentColor();
+    method public int getStatusBarGravity();
+    method public int getViewProtectionMode();
+    method public boolean isTapEventsAccepted();
+    property public final int accentColor;
+    property public final int statusBarGravity;
+    property public final boolean tapEventsAccepted;
+    property public final int viewProtectionMode;
   }
 
   public final class WatchFaceHost {
@@ -220,10 +240,10 @@
     ctor public WatchState(androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis);
     method public long getAnalogPreviewReferenceTimeMillis();
     method public long getDigitalPreviewReferenceTimeMillis();
-    method public boolean getHasBurnInProtection();
-    method public boolean getHasLowBitAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Integer> getInterruptionFilter();
     method public int getScreenShape();
+    method public boolean hasBurnInProtection();
+    method public boolean hasLowBitAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible();
     property public final long analogPreviewReferenceTimeMillis;
diff --git a/wear/wear-watchface/api/restricted_current.txt b/wear/wear-watchface/api/restricted_current.txt
index 3128c72..96c9dce 100644
--- a/wear/wear-watchface/api/restricted_current.txt
+++ b/wear/wear-watchface/api/restricted_current.txt
@@ -78,17 +78,17 @@
 
   public final class ComplicationsManager {
     ctor public ComplicationsManager(java.util.Collection<androidx.wear.watchface.Complication> complicationCollection, androidx.wear.watchface.style.UserStyleRepository userStyleRepository);
-    method @UiThread public void addTapListener(androidx.wear.watchface.ComplicationsManager.TapListener tapListener);
+    method @UiThread public void addTapListener(androidx.wear.watchface.ComplicationsManager.TapCallback tapCallback);
     method @UiThread public void bringAttentionToComplication(int complicationId);
     method public operator androidx.wear.watchface.Complication? get(int id);
     method public androidx.wear.watchface.Complication? getBackgroundComplication();
     method public androidx.wear.watchface.Complication? getComplicationAt(int x, int y);
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.Complication> getComplications();
-    method @UiThread public void removeTapListener(androidx.wear.watchface.ComplicationsManager.TapListener tapListener);
+    method @UiThread public void removeTapListener(androidx.wear.watchface.ComplicationsManager.TapCallback tapCallback);
     property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.Complication> complications;
   }
 
-  public static interface ComplicationsManager.TapListener {
+  public static interface ComplicationsManager.TapCallback {
     method public default void onComplicationDoubleTapped(int complicationId);
     method public default void onComplicationSingleTapped(int complicationId);
   }
@@ -216,19 +216,39 @@
   }
 
   public final class WatchFace {
+    method public androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle getLegacyWatchFaceStyle();
+    method public long getPreviewReferenceTimeMillis();
     method @UiThread public void invalidate();
+    method public static boolean isLegacyWatchFaceOverlayStyleSupported();
     method public void postInvalidate();
+    property public final androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle legacyWatchFaceStyle;
+    property public final long previewReferenceTimeMillis;
+    field public static final androidx.wear.watchface.WatchFace.Companion Companion;
   }
 
   public static final class WatchFace.Builder {
     ctor public WatchFace.Builder(int watchFaceType, long interactiveUpdateRateMillis, internal androidx.wear.watchface.style.UserStyleRepository userStyleRepository, internal androidx.wear.watchface.ComplicationsManager complicationsManager, internal androidx.wear.watchface.Renderer renderer, androidx.wear.watchface.WatchFaceHost watchFaceHost, androidx.wear.watchface.WatchState watchState);
     method public androidx.wear.watchface.WatchFace build();
+    method public androidx.wear.watchface.WatchFace.Builder setLegacyWatchFaceStyle(androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle legacyWatchFaceStyle);
     method public androidx.wear.watchface.WatchFace.Builder setPreviewReferenceTimeMillis(long previewReferenceTimeMillis);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.wear.watchface.WatchFace.Builder setSystemTimeProvider(androidx.wear.watchface.WatchFace.SystemTimeProvider systemTimeProvider);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2AccentColor(@ColorInt int accentColor);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2AcceptsTapEvents(boolean acceptsTapEvents);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2StatusBarGravity(int statusBarGravity);
-    method public androidx.wear.watchface.WatchFace.Builder setWear2ViewProtectionMode(int viewProtectionMode);
+  }
+
+  public static final class WatchFace.Companion {
+    method public boolean isLegacyWatchFaceOverlayStyleSupported();
+  }
+
+  public static final class WatchFace.LegacyWatchFaceOverlayStyle {
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, @ColorInt int accentColor);
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted);
+    method public int getAccentColor();
+    method public int getStatusBarGravity();
+    method public int getViewProtectionMode();
+    method public boolean isTapEventsAccepted();
+    property public final int accentColor;
+    property public final int statusBarGravity;
+    property public final boolean tapEventsAccepted;
+    property public final int viewProtectionMode;
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static interface WatchFace.SystemTimeProvider {
@@ -268,10 +288,10 @@
     ctor public WatchState(androidx.wear.watchface.ObservableWatchData<java.lang.Integer> interruptionFilter, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isBatteryLowAndNotCharging, androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, int screenShape, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis);
     method public long getAnalogPreviewReferenceTimeMillis();
     method public long getDigitalPreviewReferenceTimeMillis();
-    method public boolean getHasBurnInProtection();
-    method public boolean getHasLowBitAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Integer> getInterruptionFilter();
     method public int getScreenShape();
+    method public boolean hasBurnInProtection();
+    method public boolean hasLowBitAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isAmbient();
     method public androidx.wear.watchface.ObservableWatchData<java.lang.Boolean> isVisible();
     property public final long analogPreviewReferenceTimeMillis;
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
index 6c7faff..2e66b98 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
@@ -133,7 +133,13 @@
             renderer,
             watchFaceHost,
             watchState
-        ).setWear2StatusBarGravity(Gravity.RIGHT or Gravity.TOP).build()
+        ).setLegacyWatchFaceStyle(
+            WatchFace.LegacyWatchFaceOverlayStyle(
+                0,
+                Gravity.RIGHT or Gravity.TOP,
+                true
+            )
+        ).build()
     }
 }
 
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
index 59b6ac5..02185b38 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
@@ -304,14 +304,14 @@
         canvasComplication.onAttach(this)
     }
 
-    internal interface InvalidateCallback {
+    internal interface InvalidateListener {
         /** Requests redraw. */
         @UiThread
         fun onInvalidate()
     }
 
     private lateinit var complicationsManager: ComplicationsManager
-    private lateinit var invalidateCallback: InvalidateCallback
+    private lateinit var invalidateListener: InvalidateListener
 
     private var _unitSquareBounds = unitSquareBounds
     internal var unitSquareBoundsDirty = true
@@ -486,15 +486,15 @@
      * loading a [Drawable].
      */
     public fun invalidate() {
-        invalidateCallback.onInvalidate()
+        invalidateListener.onInvalidate()
     }
 
     internal fun init(
         complicationsManager: ComplicationsManager,
-        invalidateCallback: InvalidateCallback
+        invalidateListener: InvalidateListener
     ) {
         this.complicationsManager = complicationsManager
-        this.invalidateCallback = invalidateCallback
+        this.invalidateListener = invalidateListener
     }
 
     internal fun scheduleUpdateComplications() {
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsManager.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsManager.kt
index da6da1e..e97f277 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsManager.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsManager.kt
@@ -59,7 +59,7 @@
      */
     private val userStyleRepository: UserStyleRepository
 ) {
-    public interface TapListener {
+    public interface TapCallback {
         /**
          * Called when the user single taps on a complication.
          *
@@ -112,7 +112,7 @@
             }
         )
 
-    private val complicationListeners = HashSet<TapListener>()
+    private val complicationListeners = HashSet<TapCallback>()
 
     @VisibleForTesting
     internal constructor(
@@ -127,7 +127,7 @@
         watchFaceHostApi: WatchFaceHostApi,
         calendar: Calendar,
         renderer: Renderer,
-        complicationInvalidateCallback: Complication.InvalidateCallback
+        complicationInvalidateListener: Complication.InvalidateListener
     ) {
         this.watchFaceHostApi = watchFaceHostApi
         this.calendar = calendar
@@ -135,7 +135,7 @@
         pendingUpdate = CancellableUniqueTask(watchFaceHostApi.getHandler())
 
         for ((_, complication) in complications) {
-            complication.init(this, complicationInvalidateCallback)
+            complication.init(this, complicationInvalidateListener)
         }
 
         val complicationsStyleCategory =
@@ -408,20 +408,20 @@
     }
 
     /**
-     * Adds a [TapListener] which is called whenever the user interacts with a
+     * Adds a [TapCallback] which is called whenever the user interacts with a
      * complication.
      */
     @UiThread
     @SuppressLint("ExecutorRegistration")
-    public fun addTapListener(tapListener: TapListener) {
-        complicationListeners.add(tapListener)
+    public fun addTapListener(tapCallback: TapCallback) {
+        complicationListeners.add(tapCallback)
     }
 
     /**
-     * Removes a [TapListener] previously added by [addTapListener].
+     * Removes a [TapCallback] previously added by [addTapListener].
      */
     @UiThread
-    public fun removeTapListener(tapListener: TapListener) {
-        complicationListeners.remove(tapListener)
+    public fun removeTapListener(tapCallback: TapCallback) {
+        complicationListeners.remove(tapCallback)
     }
 }
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index e49580f..1b6cc48 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -37,6 +37,7 @@
 import androidx.annotation.VisibleForTesting
 import androidx.wear.complications.SystemProviders
 import androidx.wear.complications.data.ComplicationData
+import androidx.wear.watchface.control.IInteractiveWatchFaceSysUI
 import androidx.wear.watchface.data.RenderParametersWireFormat
 import androidx.wear.watchface.style.UserStyle
 import androidx.wear.watchface.style.UserStyleRepository
@@ -130,6 +131,62 @@
     }
 
     /**
+     * Legacy Wear 2.0 watch face styling. These settings will be ignored on Wear 3.0 devices.
+     *
+     * @throws IllegalArgumentException if [viewProtectionMode] has an unexpected value
+     */
+    public class LegacyWatchFaceOverlayStyle @JvmOverloads constructor(
+        /**
+         * The view protection mode bit field, must be a combination of
+         *     zero or more of [PROTECT_STATUS_BAR], [PROTECT_HOTWORD_INDICATOR],
+         *     [PROTECT_WHOLE_SCREEN].
+         */
+        public val viewProtectionMode: Int,
+
+        /**
+         * Controls the position of status icons (battery state, lack of connection) on the screen.
+         *
+         * This must be any combination of horizontal Gravity constant
+         *     ([Gravity.LEFT], [Gravity.CENTER_HORIZONTAL], [Gravity.RIGHT])
+         *     and vertical Gravity constants ([Gravity.TOP], [Gravity,CENTER_VERTICAL},
+         *     [Gravity,BOTTOM]), e.g. {@code Gravity.LEFT | Gravity.BOTTOM}. On circular screens,
+         *     only the vertical gravity is respected.
+         */
+        public val statusBarGravity: Int,
+
+        /**
+         * Controls whether this watch face accepts tap events.
+         *
+         * Watchfaces that set this {@code true} are indicating they are prepared to receive
+         * [IInteractiveWatchFaceSysUI.TAP_TYPE_TOUCH],
+         * [IInteractiveWatchFaceSysUI.TAP_TYPE_TOUCH_CANCEL], and
+         * [IInteractiveWatchFaceSysUI.TAP_TYPE_TAP] events.
+         */
+        @get:JvmName("isTapEventsAccepted")
+        public val tapEventsAccepted: Boolean,
+
+        /**
+         * The accent color which will be used when drawing the unread notification indicator.
+         * Default color is white.
+         */
+        @ColorInt
+        public val accentColor: Int = WatchFaceStyle.DEFAULT_ACCENT_COLOR
+    ) {
+        init {
+            if (viewProtectionMode < 0 ||
+                viewProtectionMode >
+                WatchFaceStyle.PROTECT_STATUS_BAR + WatchFaceStyle.PROTECT_HOTWORD_INDICATOR +
+                WatchFaceStyle.PROTECT_WHOLE_SCREEN
+            ) {
+                throw IllegalArgumentException(
+                    "View protection must be combination " +
+                        "PROTECT_STATUS_BAR, PROTECT_HOTWORD_INDICATOR or PROTECT_WHOLE_SCREEN"
+                )
+            }
+        }
+    }
+
+    /**
      * Builder for a [WatchFace].
      *
      * If unreadCountIndicator or notificationIndicator are hidden then the WatchState class will
@@ -167,13 +224,14 @@
          */
         private val watchState: WatchState
     ) {
-        private var viewProtectionMode: Int = 0
-        private var statusBarGravity: Int = 0
         private var overridePreviewReferenceTimeMillis: Long? = null
 
-        @ColorInt
-        private var accentColor: Int = WatchFaceStyle.DEFAULT_ACCENT_COLOR
-        private var acceptsTapEvents: Boolean = true
+        private var legacyWatchFaceStyle = LegacyWatchFaceOverlayStyle(
+            0,
+            0,
+            true
+        )
+
         private var systemTimeProvider: SystemTimeProvider = object : SystemTimeProvider {
             override fun getSystemTimeMillis() = System.currentTimeMillis()
         }
@@ -190,70 +248,12 @@
         }
 
         /**
-         * Only has an impact on devices running Wear 2.x, on other devices this is a no-op and the
-         * functionality is replaced by... TODO(alexclarke): Design the replacement.
-         *
-         * @param viewProtectionMode The view protection mode bit field, must be a combination of
-         *     zero or more of [PROTECT_STATUS_BAR], [PROTECT_HOTWORD_INDICATOR],
-         *     [PROTECT_WHOLE_SCREEN].
-         * @throws IllegalArgumentException if viewProtectionMode has an unexpected value
+         * Sets the legacy [LegacyWatchFaceOverlayStyle] which only affects Wear 2.0 devices.
          */
-        public fun setWear2ViewProtectionMode(viewProtectionMode: Int): Builder = apply {
-            if (viewProtectionMode < 0 ||
-                viewProtectionMode >
-                WatchFaceStyle.PROTECT_STATUS_BAR + WatchFaceStyle.PROTECT_HOTWORD_INDICATOR +
-                WatchFaceStyle.PROTECT_WHOLE_SCREEN
-            ) {
-                throw IllegalArgumentException(
-                    "View protection must be combination " +
-                        "PROTECT_STATUS_BAR, PROTECT_HOTWORD_INDICATOR or PROTECT_WHOLE_SCREEN"
-                )
-            }
-            this.viewProtectionMode = viewProtectionMode
-        }
-
-        /**
-         * Sets position of status icons (battery state, lack of connection) on the screen.
-         *
-         * <p>Only has an impact on devices running Wear 2.x, on other devices this is a no-op and
-         * the functionality is replaced by... TODO(alexclarke): Design the replacement.
-         *
-         * @param statusBarGravity This must be any combination of horizontal Gravity constant
-         *     ([Gravity.LEFT], [Gravity.CENTER_HORIZONTAL], [Gravity.RIGHT])
-         *     and vertical Gravity constants ([Gravity.TOP], [Gravity,CENTER_VERTICAL},
-         *     [Gravity,BOTTOM]), e.g. {@code Gravity.LEFT | Gravity.BOTTOM}. On circular screens,
-         *     only the vertical gravity is respected.
-         */
-        public fun setWear2StatusBarGravity(statusBarGravity: Int): Builder = apply {
-            this.statusBarGravity = statusBarGravity
-        }
-
-        /**
-         * Sets the accent color which can be set by developers to customise watch face. It will be
-         * used when drawing the unread notification indicator. Default color is white.
-         *
-         * <p>Only has an impact on devices running Wear 2.x, on other devices this is a no-op and
-         * the functionality is replaced by... TODO(alexclarke): Design the replacement.
-         */
-        public fun setWear2AccentColor(@ColorInt accentColor: Int): Builder = apply {
-            this.accentColor = accentColor
-        }
-
-        /**
-         * Sets whether this watchface accepts tap events. The default is false.
-         *
-         * <p>Only has an impact on devices running Wear 2.x, on other devices this is a no-op and
-         * the functionality is replaced by... TODO(alexclarke): Design the replacement.
-         *
-         * <p>Watchfaces that set this {@code true} are indicating they are prepared to receive
-         * [android.support.wearable.watchface.WatchFaceService.TAP_TYPE_TOUCH],
-         * [android.support.wearable.watchface.WatchFaceService.TAP_TYPE_TOUCH_CANCEL], and
-         * [android.support.wearable.watchface.WatchFaceService.TAP_TYPE_TAP] events.
-         *
-         * @param acceptsTapEvents whether to receive touch events.
-         */
-        public fun setWear2AcceptsTapEvents(acceptsTapEvents: Boolean): Builder = apply {
-            this.acceptsTapEvents = acceptsTapEvents
+        public fun setLegacyWatchFaceStyle(
+            legacyWatchFaceStyle: LegacyWatchFaceOverlayStyle
+        ): Builder = apply {
+            this.legacyWatchFaceStyle = legacyWatchFaceStyle
         }
 
         /** @hide */
@@ -280,12 +280,12 @@
                 watchState,
                 WatchFaceStyle(
                     componentName,
-                    viewProtectionMode,
-                    statusBarGravity,
-                    accentColor,
+                    legacyWatchFaceStyle.viewProtectionMode,
+                    legacyWatchFaceStyle.statusBarGravity,
+                    legacyWatchFaceStyle.accentColor,
                     false,
                     false,
-                    acceptsTapEvents
+                    legacyWatchFaceStyle.tapEventsAccepted
                 ),
                 componentName,
                 systemTimeProvider
@@ -293,7 +293,12 @@
         }
     }
 
-    internal companion object {
+    public companion object {
+        /** Returns whether [LegacyWatchFaceOverlayStyle] is supported on this device. */
+        @JvmStatic
+        public fun isLegacyWatchFaceOverlayStyleSupported(): Boolean =
+            android.os.Build.VERSION.SDK_INT <= 27
+
         internal const val NO_DEFAULT_PROVIDER = SystemProviders.NO_PROVIDER
 
         internal const val MOCK_TIME_INTENT = "androidx.wear.watchface.MockTime"
@@ -401,13 +406,25 @@
         }
     }
 
-    internal val previewReferenceTimeMillis =
+    /** The UTC reference time for editor preview images in milliseconds since the epoch. */
+    public val previewReferenceTimeMillis: Long =
         overridePreviewReferenceTimeMillis ?: when (watchFaceType) {
             WatchFaceType.ANALOG -> watchState.analogPreviewReferenceTimeMillis
             WatchFaceType.DIGITAL -> watchState.digitalPreviewReferenceTimeMillis
             else -> throw InvalidParameterException("Unrecognized watchFaceType")
         }
 
+    /**
+     * The legacy Wear 2.0 [LegacyWatchFaceOverlayStyle] for this watch face. Only affects Wear 2.0 devices.
+     */
+    public val legacyWatchFaceStyle: LegacyWatchFaceOverlayStyle
+        get() = LegacyWatchFaceOverlayStyle(
+            watchFaceStyle.viewProtectionMode,
+            watchFaceStyle.statusBarGravity,
+            watchFaceStyle.acceptsTapEvents,
+            watchFaceStyle.accentColor
+        )
+
     init {
         // If the system has a stored user style then Home/SysUI is in charge of style
         // persistence, otherwise we need to do our own.
@@ -470,7 +487,7 @@
         var initFinished = false
         complicationsManager.init(
             watchFaceHostApi, calendar, renderer,
-            object : Complication.InvalidateCallback {
+            object : Complication.InvalidateListener {
                 @SuppressWarnings("SyntheticAccessor")
                 override fun onInvalidate() {
                     // Ensure we render a frame if the Complication needs rendering, e.g. because it
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
index 554507e..a1d4d01 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
@@ -58,18 +58,19 @@
      * Sets ContentDescriptionLabels for text-to-speech screen readers to make your
      * complications, buttons, and any other text on your watchface accessible.
      *
-     * <p>Each label is a region of the screen in absolute coordinates, along with
+     * Each label is a region of the screen in absolute coordinates, along with
      * time-dependent text. The regions must not overlap.
      *
-     * <p>You must set all labels at the same time; previous labels will be cleared. An empty
+     * You must set all labels at the same time; previous labels will be cleared. An empty
      * array clears all labels.
      *
-     * <p>In addition to labeling your complications, please include a label that will read the
+     * In addition to labeling your complications, please include a label that will read the
      * current time. You can use [android.support.wearable.watchface.accessibility
-     * .AccessibilityUtils.makeTimeAsComplicationText] to generate the proper ComplicationText.
+     * .AccessibilityUtils.makeTimeAsComplicationText] to generate the proper
+     * [android.support.wearable.complications.ComplicationText].
      *
-     * <p>This is a fairly expensive operation so use it sparingly (e.g. do not call it in
-     * onDraw()).
+     * This is a fairly expensive operation so use it sparingly (e.g. do not call it in
+     * `onDraw()`).
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public fun setContentDescriptionLabels(labels: Array<ContentDescriptionLabel>)
@@ -78,13 +79,13 @@
      * Sets the complications which are active in the watchface. Complication data will be
      * received for these ids.
      *
-     * <p>Any ids not in the provided {@code ids} will be considered inactive.
+     * Any ids not in the provided [watchFaceComplicationIds] will be considered inactive.
      *
-     * <p>If providers and complication data types have been configured, the data received will
+     * If providers and complication data types have been configured, the data received will
      * match the type chosen by the user. If no provider has been configured, data of type
      * [ComplicationData.TYPE_NOT_CONFIGURED] will be received.
      *
-     * <p>Ids here are chosen by the watch face to represent each complication and can be any
+     * Ids here are chosen by the watch face to represent each complication and can be any
      * integer.
      */
     public fun setActiveComplications(watchFaceComplicationIds: IntArray)
@@ -95,18 +96,18 @@
      * first doesn't exist then the next one is tried and so on. If none of them exist then the
      * specified system provider is set as the default instead.
      *
-     * <p>This will do nothing if the providers are not installed, or if the specified type is
+     * This will do nothing if the providers are not installed, or if the specified type is
      * not supported by the providers, or if the user has already selected a provider for the
      * complication.
      *
-     * <p>Note that if the watch face has not yet been granted the RECEIVE_COMPLICATION_DATA
+     * Note that if the watch face has not yet been granted the `RECEIVE_COMPLICATION_DATA`
      * permission, it will not be able to receive data from the provider unless the provider is
      * from the same app package as the watch face, or the provider lists the watch face as a
      * safe watch face. For system providers that may be used before your watch face has the
      * permission, use [.setDefaultSystemComplicationProvider] with a safe provider
      * instead.
      *
-     * <p>A provider not satisfying the above conditions may still be set as a default using
+     * A provider not satisfying the above conditions may still be set as a default using
      * this method, but the watch face will receive placeholder data of type
      * [ComplicationData.TYPE_NO_PERMISSION] until the permission has been granted.
      *
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
index 4f18d4d..e48eb3c 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
@@ -73,9 +73,11 @@
     public val isVisible: ObservableWatchData<Boolean>,
 
     /** Whether or not the watch hardware supports low bit ambient support. */
+    @get:JvmName("hasLowBitAmbient")
     public val hasLowBitAmbient: Boolean,
 
     /** Whether or not the watch hardware supports burn in protection. */
+    @get:JvmName("hasBurnInProtection")
     public val hasBurnInProtection: Boolean,
 
     /** The physical shape of the screen. */
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
index cf39c93..886cbeb 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
@@ -62,7 +62,7 @@
         )
 
         complicationsManager.addTapListener(
-            object : ComplicationsManager.TapListener {
+            object : ComplicationsManager.TapCallback {
                 override fun onComplicationSingleTapped(complicationId: Int) {
                     complicationSingleTapped = complicationId
                 }
diff --git a/window/window-extensions/api/current.txt b/window/window-extensions/api/current.txt
index 12ad1d9..01608ca 100644
--- a/window/window-extensions/api/current.txt
+++ b/window/window-extensions/api/current.txt
@@ -21,14 +21,14 @@
 
   public interface ExtensionInterface {
     method public void onDeviceStateListenersChanged(boolean);
-    method public void onWindowLayoutChangeListenerAdded(android.content.Context);
-    method public void onWindowLayoutChangeListenerRemoved(android.content.Context);
+    method public void onWindowLayoutChangeListenerAdded(android.app.Activity);
+    method public void onWindowLayoutChangeListenerRemoved(android.app.Activity);
     method public void setExtensionCallback(androidx.window.extensions.ExtensionInterface.ExtensionCallback);
   }
 
   public static interface ExtensionInterface.ExtensionCallback {
     method public void onDeviceStateChanged(androidx.window.extensions.ExtensionDeviceState);
-    method public void onWindowLayoutChanged(android.content.Context, androidx.window.extensions.ExtensionWindowLayoutInfo);
+    method public void onWindowLayoutChanged(android.app.Activity, androidx.window.extensions.ExtensionWindowLayoutInfo);
   }
 
   public final class ExtensionProvider {
diff --git a/window/window-extensions/api/public_plus_experimental_current.txt b/window/window-extensions/api/public_plus_experimental_current.txt
index 12ad1d9..01608ca 100644
--- a/window/window-extensions/api/public_plus_experimental_current.txt
+++ b/window/window-extensions/api/public_plus_experimental_current.txt
@@ -21,14 +21,14 @@
 
   public interface ExtensionInterface {
     method public void onDeviceStateListenersChanged(boolean);
-    method public void onWindowLayoutChangeListenerAdded(android.content.Context);
-    method public void onWindowLayoutChangeListenerRemoved(android.content.Context);
+    method public void onWindowLayoutChangeListenerAdded(android.app.Activity);
+    method public void onWindowLayoutChangeListenerRemoved(android.app.Activity);
     method public void setExtensionCallback(androidx.window.extensions.ExtensionInterface.ExtensionCallback);
   }
 
   public static interface ExtensionInterface.ExtensionCallback {
     method public void onDeviceStateChanged(androidx.window.extensions.ExtensionDeviceState);
-    method public void onWindowLayoutChanged(android.content.Context, androidx.window.extensions.ExtensionWindowLayoutInfo);
+    method public void onWindowLayoutChanged(android.app.Activity, androidx.window.extensions.ExtensionWindowLayoutInfo);
   }
 
   public final class ExtensionProvider {
diff --git a/window/window-extensions/api/restricted_current.txt b/window/window-extensions/api/restricted_current.txt
index 12ad1d9..01608ca 100644
--- a/window/window-extensions/api/restricted_current.txt
+++ b/window/window-extensions/api/restricted_current.txt
@@ -21,14 +21,14 @@
 
   public interface ExtensionInterface {
     method public void onDeviceStateListenersChanged(boolean);
-    method public void onWindowLayoutChangeListenerAdded(android.content.Context);
-    method public void onWindowLayoutChangeListenerRemoved(android.content.Context);
+    method public void onWindowLayoutChangeListenerAdded(android.app.Activity);
+    method public void onWindowLayoutChangeListenerRemoved(android.app.Activity);
     method public void setExtensionCallback(androidx.window.extensions.ExtensionInterface.ExtensionCallback);
   }
 
   public static interface ExtensionInterface.ExtensionCallback {
     method public void onDeviceStateChanged(androidx.window.extensions.ExtensionDeviceState);
-    method public void onWindowLayoutChanged(android.content.Context, androidx.window.extensions.ExtensionWindowLayoutInfo);
+    method public void onWindowLayoutChanged(android.app.Activity, androidx.window.extensions.ExtensionWindowLayoutInfo);
   }
 
   public final class ExtensionProvider {
diff --git a/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionInterface.java b/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionInterface.java
index 4926c63..6c809ae 100644
--- a/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionInterface.java
+++ b/window/window-extensions/src/main/java/androidx/window/extensions/ExtensionInterface.java
@@ -16,7 +16,7 @@
 
 package androidx.window.extensions;
 
-import android.content.Context;
+import android.app.Activity;
 
 import androidx.annotation.NonNull;
 
@@ -38,19 +38,15 @@
 
     /**
      * Notifies extension that a listener for display feature layout changes was registered for the
-     * given activity context.
-     *
-     * @param context an instance of {@link android.app.Activity}
+     * given {@link Activity} context.
      */
-    void onWindowLayoutChangeListenerAdded(@NonNull Context context);
+    void onWindowLayoutChangeListenerAdded(@NonNull Activity activity);
 
     /**
      * Notifies extension that a listener for display feature layout changes was removed for the
-     * given activity context.
-     *
-     * @param context an instance of {@link android.app.Activity}
+     * given {@link Activity} context.
      */
-    void onWindowLayoutChangeListenerRemoved(@NonNull Context context);
+    void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity);
 
     /**
      * Notifies the extension that a device state change listener was updated.
@@ -75,7 +71,7 @@
          * Called by extension when the feature layout inside the window changes. Initial value
          * should be provided as soon as possible.
          */
-        void onWindowLayoutChanged(@NonNull Context context,
+        void onWindowLayoutChanged(@NonNull Activity activity,
                 @NonNull ExtensionWindowLayoutInfo newLayout);
     }
 }
diff --git a/window/window-extensions/src/main/java/androidx/window/extensions/StubExtension.java b/window/window-extensions/src/main/java/androidx/window/extensions/StubExtension.java
index 3c4532a..17751fd 100644
--- a/window/window-extensions/src/main/java/androidx/window/extensions/StubExtension.java
+++ b/window/window-extensions/src/main/java/androidx/window/extensions/StubExtension.java
@@ -16,7 +16,7 @@
 
 package androidx.window.extensions;
 
-import android.content.Context;
+import android.app.Activity;
 
 import androidx.annotation.NonNull;
 
@@ -29,7 +29,7 @@
  */
 abstract class StubExtension implements ExtensionInterface {
     private ExtensionCallback mExtensionCallback;
-    private final Set<Context> mWindowLayoutChangeListenerContexts = new HashSet<>();
+    private final Set<Activity> mWindowLayoutChangeListenerContexts = new HashSet<>();
     private boolean mDeviceStateChangeListenerRegistered;
 
     @Override
@@ -38,14 +38,14 @@
     }
 
     @Override
-    public void onWindowLayoutChangeListenerAdded(@NonNull Context context) {
-        mWindowLayoutChangeListenerContexts.add(context);
+    public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
+        mWindowLayoutChangeListenerContexts.add(activity);
         onListenersChanged();
     }
 
     @Override
-    public void onWindowLayoutChangeListenerRemoved(@NonNull Context context) {
-        mWindowLayoutChangeListenerContexts.remove(context);
+    public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
+        mWindowLayoutChangeListenerContexts.remove(activity);
         onListenersChanged();
     }
 
@@ -61,15 +61,15 @@
         }
     }
 
-    protected void updateWindowLayout(@NonNull Context context,
+    protected void updateWindowLayout(@NonNull Activity activity,
             @NonNull ExtensionWindowLayoutInfo newLayout) {
         if (mExtensionCallback != null) {
-            mExtensionCallback.onWindowLayoutChanged(context, newLayout);
+            mExtensionCallback.onWindowLayoutChanged(activity, newLayout);
         }
     }
 
     @NonNull
-    protected Set<Context> getWindowsListeningForLayoutChanges() {
+    protected Set<Activity> getWindowsListeningForLayoutChanges() {
         return mWindowLayoutChangeListenerContexts;
     }
 
diff --git a/window/window-samples/src/main/java/androidx/window/sample/backend/ContextExtensions.kt b/window/window-samples/src/main/java/androidx/window/sample/backend/ContextExtensions.kt
deleted file mode 100644
index fffe251..0000000
--- a/window/window-samples/src/main/java/androidx/window/sample/backend/ContextExtensions.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.sample.backend
-
-import android.app.Activity
-import android.content.Context
-import android.content.ContextWrapper
-
-/**
- * Unwraps the hierarchy of [ContextWrapper]-s until [Activity] is reached.
- * @return Base [Activity] context or {@code null} if not available.
- */
-internal fun Context.getActivityExt(): Activity? {
-    var context = this
-    while (context is ContextWrapper) {
-        if (context is Activity) {
-            return context
-        }
-        context = context.baseContext
-    }
-    return null
-}
\ No newline at end of file
diff --git a/window/window-samples/src/main/java/androidx/window/sample/backend/MidScreenFoldBackend.kt b/window/window-samples/src/main/java/androidx/window/sample/backend/MidScreenFoldBackend.kt
index 512c76b..5ee6df2 100644
--- a/window/window-samples/src/main/java/androidx/window/sample/backend/MidScreenFoldBackend.kt
+++ b/window/window-samples/src/main/java/androidx/window/sample/backend/MidScreenFoldBackend.kt
@@ -16,7 +16,7 @@
 
 package androidx.window.sample.backend
 
-import android.content.Context
+import android.app.Activity
 import android.graphics.Point
 import android.graphics.Rect
 import androidx.core.util.Consumer
@@ -57,11 +57,7 @@
         return DeviceState.Builder().setPosture(DeviceState.POSTURE_OPENED).build()
     }
 
-    private fun getWindowLayoutInfo(context: Context): WindowLayoutInfo {
-        val activity = context.getActivityExt() ?: throw IllegalArgumentException(
-            "Used non-visual Context used with WindowManager. Please use an Activity or a " +
-                "ContextWrapper around an Activity instead."
-        )
+    private fun getWindowLayoutInfo(activity: Activity): WindowLayoutInfo {
         val windowSize = activity.calculateWindowSizeExt()
         val featureRect = foldRect(windowSize)
 
@@ -108,11 +104,11 @@
     }
 
     override fun registerLayoutChangeCallback(
-        context: Context,
+        activity: Activity,
         executor: Executor,
         callback: Consumer<WindowLayoutInfo>
     ) {
-        executor.execute { callback.accept(getWindowLayoutInfo(context)) }
+        executor.execute { callback.accept(getWindowLayoutInfo(activity)) }
     }
 
     override fun unregisterLayoutChangeCallback(callback: Consumer<WindowLayoutInfo>) {
diff --git a/window/window/api/current.txt b/window/window/api/current.txt
index 2741333..3abcf8b 100644
--- a/window/window/api/current.txt
+++ b/window/window/api/current.txt
@@ -32,7 +32,7 @@
 
   public interface WindowBackend {
     method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
-    method public void registerLayoutChangeCallback(android.content.Context, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
+    method public void registerLayoutChangeCallback(android.app.Activity, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
     method public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
   }
diff --git a/window/window/api/public_plus_experimental_current.txt b/window/window/api/public_plus_experimental_current.txt
index 2741333..3abcf8b 100644
--- a/window/window/api/public_plus_experimental_current.txt
+++ b/window/window/api/public_plus_experimental_current.txt
@@ -32,7 +32,7 @@
 
   public interface WindowBackend {
     method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
-    method public void registerLayoutChangeCallback(android.content.Context, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
+    method public void registerLayoutChangeCallback(android.app.Activity, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
     method public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
   }
diff --git a/window/window/api/restricted_current.txt b/window/window/api/restricted_current.txt
index 2741333..3abcf8b 100644
--- a/window/window/api/restricted_current.txt
+++ b/window/window/api/restricted_current.txt
@@ -32,7 +32,7 @@
 
   public interface WindowBackend {
     method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
-    method public void registerLayoutChangeCallback(android.content.Context, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
+    method public void registerLayoutChangeCallback(android.app.Activity, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
     method public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
     method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
   }
diff --git a/window/window/src/androidTest/java/androidx/window/ExtensionAdapterTest.java b/window/window/src/androidTest/java/androidx/window/ExtensionAdapterTest.java
index f6e7f1f..c6e6348 100644
--- a/window/window/src/androidTest/java/androidx/window/ExtensionAdapterTest.java
+++ b/window/window/src/androidTest/java/androidx/window/ExtensionAdapterTest.java
@@ -62,7 +62,7 @@
     @Test
     @Override
     public void testTranslate_validFeature() {
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
         Rect bounds = new Rect(WINDOW_BOUNDS.left, 0, WINDOW_BOUNDS.right, 0);
         ExtensionDisplayFeature foldFeature = new ExtensionDisplayFeature(bounds,
                 ExtensionDisplayFeature.TYPE_FOLD);
@@ -78,7 +78,7 @@
 
         ExtensionAdapter adapter = new ExtensionAdapter();
 
-        WindowLayoutInfo actual = adapter.translate(mockContext, windowLayoutInfo);
+        WindowLayoutInfo actual = adapter.translate(mockActivity, windowLayoutInfo);
 
         assertEquals(expected, actual);
     }
@@ -93,9 +93,9 @@
         ExtensionAdapter adapter = new ExtensionAdapter();
         ExtensionWindowLayoutInfo windowLayoutInfo =
                 new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        WindowLayoutInfo actual = adapter.translate(mockContext, windowLayoutInfo);
+        WindowLayoutInfo actual = adapter.translate(mockActivity, windowLayoutInfo);
 
         assertTrue("Remove empty bounds feature", actual.getDisplayFeatures().isEmpty());
     }
@@ -115,9 +115,10 @@
         ExtensionAdapter extensionCallbackAdapter = new ExtensionAdapter();
         ExtensionWindowLayoutInfo windowLayoutInfo =
                 new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        WindowLayoutInfo actual = extensionCallbackAdapter.translate(mockContext, windowLayoutInfo);
+        WindowLayoutInfo actual = extensionCallbackAdapter.translate(mockActivity,
+                windowLayoutInfo);
 
         assertTrue("Remove non empty area fold feature", actual.getDisplayFeatures().isEmpty());
     }
@@ -141,9 +142,10 @@
         ExtensionWindowLayoutInfo windowLayoutInfo =
                 new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
 
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        WindowLayoutInfo actual = extensionCallbackAdapter.translate(mockContext, windowLayoutInfo);
+        WindowLayoutInfo actual = extensionCallbackAdapter.translate(mockActivity,
+                windowLayoutInfo);
 
         assertTrue("Remove hinge feature not spanning full dimension",
                 actual.getDisplayFeatures().isEmpty());
@@ -166,9 +168,9 @@
         ExtensionWindowLayoutInfo windowLayoutInfo =
                 new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
 
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        WindowLayoutInfo actual = adapter.translate(mockContext, windowLayoutInfo);
+        WindowLayoutInfo actual = adapter.translate(mockActivity, windowLayoutInfo);
 
         assertTrue("Remove fold feature not spanning full dimension",
                 actual.getDisplayFeatures().isEmpty());
diff --git a/window/window/src/androidTest/java/androidx/window/ExtensionCompatTest.java b/window/window/src/androidTest/java/androidx/window/ExtensionCompatTest.java
index c1aaf670..f2d1f92 100644
--- a/window/window/src/androidTest/java/androidx/window/ExtensionCompatTest.java
+++ b/window/window/src/androidTest/java/androidx/window/ExtensionCompatTest.java
@@ -30,7 +30,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
-import android.content.Context;
 import android.graphics.Rect;
 
 import androidx.annotation.NonNull;
@@ -167,7 +166,7 @@
         ExtensionCompat compat = new ExtensionCompat(fakeExtensionImp, new ExtensionAdapter());
         ExtensionCallbackInterface mockCallback = mock(ExtensionCallbackInterface.class);
         compat.setExtensionCallback(mockCallback);
-        compat.onWindowLayoutChangeListenerAdded(mock(Context.class));
+        compat.onWindowLayoutChangeListenerAdded(mock(Activity.class));
 
         fakeExtensionImp.triggerMalformedSignal();
 
@@ -205,7 +204,7 @@
     private static final class FakeExtensionImp implements ExtensionInterface {
 
         private ExtensionCallback mCallback;
-        private final List<Context> mContexts = new ArrayList<>();
+        private final List<Activity> mActivities = new ArrayList<>();
 
         FakeExtensionImp() {
             mCallback = new ExtensionCallback() {
@@ -215,7 +214,7 @@
                 }
 
                 @Override
-                public void onWindowLayoutChanged(@NonNull Context context,
+                public void onWindowLayoutChanged(@NonNull Activity activity,
                         @NonNull ExtensionWindowLayoutInfo newLayout) {
 
                 }
@@ -228,13 +227,13 @@
         }
 
         @Override
-        public void onWindowLayoutChangeListenerAdded(@NonNull Context context) {
-            mContexts.add(context);
+        public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
+            mActivities.add(activity);
         }
 
         @Override
-        public void onWindowLayoutChangeListenerRemoved(@NonNull Context context) {
-            mContexts.remove(context);
+        public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
+            mActivities.remove(activity);
         }
 
         @Override
@@ -251,8 +250,8 @@
         }
 
         void triggerSignal(ExtensionWindowLayoutInfo info) {
-            for (Context context: mContexts) {
-                mCallback.onWindowLayoutChanged(context, info);
+            for (Activity activity: mActivities) {
+                mCallback.onWindowLayoutChanged(activity, info);
             }
         }
 
diff --git a/window/window/src/androidTest/java/androidx/window/ExtensionTranslatingCallbackTest.java b/window/window/src/androidTest/java/androidx/window/ExtensionTranslatingCallbackTest.java
index aeb2a82..09a9305 100644
--- a/window/window/src/androidTest/java/androidx/window/ExtensionTranslatingCallbackTest.java
+++ b/window/window/src/androidTest/java/androidx/window/ExtensionTranslatingCallbackTest.java
@@ -60,7 +60,7 @@
 
     @Test
     public void testOnWindowLayoutChange_validFeature() {
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
         Rect bounds = new Rect(WINDOW_BOUNDS.left, 0, WINDOW_BOUNDS.right, 0);
         ExtensionDisplayFeature foldFeature = new ExtensionDisplayFeature(bounds,
                 ExtensionDisplayFeature.TYPE_FOLD);
@@ -78,10 +78,10 @@
         ExtensionTranslatingCallback extensionTranslatingCallback =
                 new ExtensionTranslatingCallback(mockCallback, new ExtensionAdapter());
 
-        extensionTranslatingCallback.onWindowLayoutChanged(mockContext, windowLayoutInfo);
+        extensionTranslatingCallback.onWindowLayoutChanged(mockActivity, windowLayoutInfo);
 
         ArgumentCaptor<WindowLayoutInfo> captor = ArgumentCaptor.forClass(WindowLayoutInfo.class);
-        verify(mockCallback).onWindowLayoutChanged(eq(mockContext), captor.capture());
+        verify(mockCallback).onWindowLayoutChanged(eq(mockActivity), captor.capture());
         assertEquals(expected, captor.getValue());
     }
 
@@ -96,11 +96,11 @@
                 new ExtensionTranslatingCallback(mockCallback, new ExtensionAdapter());
         ExtensionWindowLayoutInfo windowLayoutInfo =
                 new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        extensionTranslatingCallback.onWindowLayoutChanged(mockContext, windowLayoutInfo);
+        extensionTranslatingCallback.onWindowLayoutChanged(mockActivity, windowLayoutInfo);
 
-        verify(mockCallback).onWindowLayoutChanged(eq(mockContext),
+        verify(mockCallback).onWindowLayoutChanged(eq(mockActivity),
                 argThat((layoutInfo) -> layoutInfo.getDisplayFeatures().isEmpty()));
     }
 
@@ -120,11 +120,11 @@
                 new ExtensionTranslatingCallback(mockCallback, new ExtensionAdapter());
         ExtensionWindowLayoutInfo windowLayoutInfo =
                 new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        extensionTranslatingCallback.onWindowLayoutChanged(mockContext, windowLayoutInfo);
+        extensionTranslatingCallback.onWindowLayoutChanged(mockActivity, windowLayoutInfo);
 
-        verify(mockCallback).onWindowLayoutChanged(eq(mockContext),
+        verify(mockCallback).onWindowLayoutChanged(eq(mockActivity),
                 argThat((layoutInfo) -> layoutInfo.getDisplayFeatures().isEmpty()));
     }
 
@@ -146,11 +146,11 @@
         ExtensionWindowLayoutInfo windowLayoutInfo =
                 new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
 
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        extensionTranslatingCallback.onWindowLayoutChanged(mockContext, windowLayoutInfo);
+        extensionTranslatingCallback.onWindowLayoutChanged(mockActivity, windowLayoutInfo);
 
-        verify(mockCallback).onWindowLayoutChanged(eq(mockContext),
+        verify(mockCallback).onWindowLayoutChanged(eq(mockActivity),
                 argThat((layoutInfo) -> layoutInfo.getDisplayFeatures().isEmpty()));
     }
 
@@ -172,11 +172,11 @@
         ExtensionWindowLayoutInfo windowLayoutInfo =
                 new ExtensionWindowLayoutInfo(extensionDisplayFeatures);
 
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        extensionTranslatingCallback.onWindowLayoutChanged(mockContext, windowLayoutInfo);
+        extensionTranslatingCallback.onWindowLayoutChanged(mockActivity, windowLayoutInfo);
 
-        verify(mockCallback).onWindowLayoutChanged(eq(mockContext),
+        verify(mockCallback).onWindowLayoutChanged(eq(mockActivity),
                 argThat((layoutInfo) -> layoutInfo.getDisplayFeatures().isEmpty()));
     }
 
diff --git a/window/window/src/androidTest/java/androidx/window/ExtensionWindowBackendTest.java b/window/window/src/androidTest/java/androidx/window/ExtensionWindowBackendTest.java
index c7175a5..2361ed1 100644
--- a/window/window/src/androidTest/java/androidx/window/ExtensionWindowBackendTest.java
+++ b/window/window/src/androidTest/java/androidx/window/ExtensionWindowBackendTest.java
@@ -50,7 +50,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.Executor;
 
 /** Tests for {@link ExtensionWindowBackend} class. */
 @LargeTest
@@ -204,15 +203,6 @@
         verify(consumer).accept(expectedWindowLayoutInfo);
     }
 
-    @Test(expected = IllegalArgumentException.class)
-    public void testRegisterLayoutChangeCallback_applicationContext() {
-        ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
-        backend.mWindowExtension = mock(ExtensionInterfaceCompat.class);
-
-        backend.registerLayoutChangeCallback(mContext, mock(Executor.class),
-                mock(Consumer.class));
-    }
-
     @Test
     public void testLayoutChangeCallback_emitNewValue() {
         ExtensionWindowBackend backend = ExtensionWindowBackend.getInstance(mContext);
diff --git a/window/window/src/androidTest/java/androidx/window/SidecarAdapterTest.java b/window/window/src/androidTest/java/androidx/window/SidecarAdapterTest.java
index 3d19204..65e2903 100644
--- a/window/window/src/androidTest/java/androidx/window/SidecarAdapterTest.java
+++ b/window/window/src/androidTest/java/androidx/window/SidecarAdapterTest.java
@@ -76,7 +76,7 @@
     @Test
     @Override
     public void testTranslate_validFeature() {
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
         Rect bounds = new Rect(WINDOW_BOUNDS.left, 0, WINDOW_BOUNDS.right, 0);
         SidecarDisplayFeature foldFeature = sidecarDisplayFeature(bounds,
                 SidecarDisplayFeature.TYPE_FOLD);
@@ -91,7 +91,7 @@
 
         SidecarAdapter sidecarAdapter = new SidecarAdapter();
 
-        WindowLayoutInfo actual = sidecarAdapter.translate(mockContext, windowLayoutInfo);
+        WindowLayoutInfo actual = sidecarAdapter.translate(mockActivity, windowLayoutInfo);
 
         assertEquals(expected, actual);
     }
@@ -105,9 +105,9 @@
 
         SidecarAdapter sidecarAdapter = new SidecarAdapter();
         SidecarWindowLayoutInfo windowLayoutInfo = sidecarWindowLayoutInfo(sidecarDisplayFeatures);
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        WindowLayoutInfo actual = sidecarAdapter.translate(mockContext, windowLayoutInfo);
+        WindowLayoutInfo actual = sidecarAdapter.translate(mockActivity, windowLayoutInfo);
 
         assertTrue(actual.getDisplayFeatures().isEmpty());
     }
@@ -128,9 +128,9 @@
                 ExtensionInterfaceCompat.ExtensionCallbackInterface.class);
         SidecarAdapter sidecarCallbackAdapter = new SidecarAdapter();
         SidecarWindowLayoutInfo windowLayoutInfo = sidecarWindowLayoutInfo(sidecarDisplayFeatures);
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        WindowLayoutInfo actual = sidecarCallbackAdapter.translate(mockContext, windowLayoutInfo);
+        WindowLayoutInfo actual = sidecarCallbackAdapter.translate(mockActivity, windowLayoutInfo);
 
         assertTrue(actual.getDisplayFeatures().isEmpty());
     }
@@ -152,9 +152,9 @@
         SidecarAdapter sidecarAdapter = new SidecarAdapter();
         SidecarWindowLayoutInfo windowLayoutInfo = sidecarWindowLayoutInfo(sidecarDisplayFeatures);
 
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        WindowLayoutInfo actual = sidecarAdapter.translate(mockContext, windowLayoutInfo);
+        WindowLayoutInfo actual = sidecarAdapter.translate(mockActivity, windowLayoutInfo);
 
         assertTrue(actual.getDisplayFeatures().isEmpty());
     }
@@ -176,9 +176,9 @@
         SidecarWindowLayoutInfo windowLayoutInfo = sidecarWindowLayoutInfo(
                 extensionDisplayFeatures);
 
-        Activity mockContext = mock(Activity.class);
+        Activity mockActivity = mock(Activity.class);
 
-        WindowLayoutInfo actual = sidecarCallbackAdapter.translate(mockContext, windowLayoutInfo);
+        WindowLayoutInfo actual = sidecarCallbackAdapter.translate(mockActivity, windowLayoutInfo);
 
         assertTrue(actual.getDisplayFeatures().isEmpty());
     }
diff --git a/window/window/src/androidTest/java/androidx/window/WindowBackendTest.java b/window/window/src/androidTest/java/androidx/window/WindowBackendTest.java
index 1d9c2e8..6378d7a 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowBackendTest.java
+++ b/window/window/src/androidTest/java/androidx/window/WindowBackendTest.java
@@ -19,7 +19,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.content.Context;
+import android.app.Activity;
 import android.content.Intent;
 import android.graphics.Rect;
 
@@ -86,7 +86,7 @@
         }
 
         @Override
-        public void registerLayoutChangeCallback(@NonNull Context context,
+        public void registerLayoutChangeCallback(@NonNull Activity activity,
                 @NonNull Executor executor, @NonNull Consumer<WindowLayoutInfo> callback) {
             executor.execute(() -> callback.accept(mWindowLayoutInfo));
         }
diff --git a/window/window/src/main/java/androidx/window/ExtensionAdapter.java b/window/window/src/main/java/androidx/window/ExtensionAdapter.java
index cf032f5..c2e5549 100644
--- a/window/window/src/main/java/androidx/window/ExtensionAdapter.java
+++ b/window/window/src/main/java/androidx/window/ExtensionAdapter.java
@@ -17,7 +17,6 @@
 package androidx.window;
 
 import android.app.Activity;
-import android.content.Context;
 import android.graphics.Rect;
 
 import androidx.annotation.NonNull;
@@ -62,17 +61,16 @@
      * Translates a valid {@link ExtensionDisplayFeature} into a valid {@link DisplayFeature}. If
      * a feature is not valid it is removed
      *
-     * @param context    An {@link android.app.Activity} or a {@link android.content.ContextWrapper}
-     *                   around an {@link android.app.Activity}.
+     * @param activity   An {@link android.app.Activity}.
      * @param layoutInfo The source {@link ExtensionWindowLayoutInfo} to be converted
      * @return {@link WindowLayoutInfo} containing the valid {@link DisplayFeature}
      */
     @NonNull
-    WindowLayoutInfo translate(@NonNull Context context,
+    WindowLayoutInfo translate(@NonNull Activity activity,
             @NonNull ExtensionWindowLayoutInfo layoutInfo) {
         List<DisplayFeature> featureList = new ArrayList<>();
         for (ExtensionDisplayFeature sourceFeature : layoutInfo.getDisplayFeatures()) {
-            DisplayFeature targetFeature = translate(context, sourceFeature);
+            DisplayFeature targetFeature = translate(activity, sourceFeature);
             if (targetFeature != null) {
                 featureList.add(targetFeature);
             }
@@ -81,8 +79,9 @@
     }
 
     @Nullable
-    DisplayFeature translate(Context context, ExtensionDisplayFeature feature) {
-        final Rect windowBounds = windowBounds(context);
+    DisplayFeature translate(Activity activity, ExtensionDisplayFeature feature) {
+        final Rect windowBounds = WindowBoundsHelper.getInstance()
+                .computeCurrentWindowBounds(activity);
         if (!isValid(feature, windowBounds)) {
             return null;
         }
@@ -117,9 +116,4 @@
         boolean matchesHeight = lhs.top == rhs.top && lhs.bottom == rhs.bottom;
         return matchesWidth || matchesHeight;
     }
-
-    private Rect windowBounds(Context context) {
-        Activity activity = WindowManager.assertActivityFromContext(context);
-        return WindowBoundsHelper.getInstance().computeCurrentWindowBounds(activity);
-    }
 }
diff --git a/window/window/src/main/java/androidx/window/ExtensionCompat.java b/window/window/src/main/java/androidx/window/ExtensionCompat.java
index 083bca5..8a09172 100644
--- a/window/window/src/main/java/androidx/window/ExtensionCompat.java
+++ b/window/window/src/main/java/androidx/window/ExtensionCompat.java
@@ -16,6 +16,7 @@
 
 package androidx.window;
 
+import android.app.Activity;
 import android.content.Context;
 import android.graphics.Rect;
 import android.text.TextUtils;
@@ -65,13 +66,13 @@
     }
 
     @Override
-    public void onWindowLayoutChangeListenerAdded(@NonNull Context context) {
-        mWindowExtension.onWindowLayoutChangeListenerAdded(context);
+    public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
+        mWindowExtension.onWindowLayoutChangeListenerAdded(activity);
     }
 
     @Override
-    public void onWindowLayoutChangeListenerRemoved(@NonNull Context context) {
-        mWindowExtension.onWindowLayoutChangeListenerRemoved(context);
+    public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
+        mWindowExtension.onWindowLayoutChangeListenerRemoved(activity);
     }
 
     @Override
@@ -114,9 +115,9 @@
             // extension.onDeviceStateListenersChanged(boolean);
             mWindowExtension.onDeviceStateListenersChanged(true /* empty */);
 
-            // extension.onWindowLayoutChangeListenerAdded(Context);
+            // extension.onWindowLayoutChangeListenerAdded(Activity);
             Method methodRegisterWindowLayoutChangeListener = mWindowExtension.getClass()
-                    .getMethod("onWindowLayoutChangeListenerAdded", Context.class);
+                    .getMethod("onWindowLayoutChangeListenerAdded", Activity.class);
             Class<?> rtRegisterWindowLayoutChangeListener =
                     methodRegisterWindowLayoutChangeListener.getReturnType();
             if (!rtRegisterWindowLayoutChangeListener.equals(void.class)) {
@@ -125,9 +126,9 @@
                                 + rtRegisterWindowLayoutChangeListener);
             }
 
-            // extension.onWindowLayoutChangeListenerRemoved(Context);
+            // extension.onWindowLayoutChangeListenerRemoved(Activity);
             Method methodUnregisterWindowLayoutChangeListener = mWindowExtension.getClass()
-                    .getMethod("onWindowLayoutChangeListenerRemoved", Context.class);
+                    .getMethod("onWindowLayoutChangeListenerRemoved", Activity.class);
             Class<?> rtUnregisterWindowLayoutChangeListener =
                     methodUnregisterWindowLayoutChangeListener.getReturnType();
             if (!rtUnregisterWindowLayoutChangeListener.equals(void.class)) {
diff --git a/window/window/src/main/java/androidx/window/ExtensionInterfaceCompat.java b/window/window/src/main/java/androidx/window/ExtensionInterfaceCompat.java
index 07be11e..fe4d703 100644
--- a/window/window/src/main/java/androidx/window/ExtensionInterfaceCompat.java
+++ b/window/window/src/main/java/androidx/window/ExtensionInterfaceCompat.java
@@ -16,7 +16,7 @@
 
 package androidx.window;
 
-import android.content.Context;
+import android.app.Activity;
 
 import androidx.annotation.NonNull;
 
@@ -39,16 +39,16 @@
 
     /**
      * Notifies extension that a listener for display feature layout changes was registered for the
-     * given activity or window context. Should notify the {@link ExtensionCallbackInterface} of
+     * given activity context. Should notify the {@link ExtensionCallbackInterface} of
      * the initial {@link WindowLayoutInfo} when it is available.
      */
-    void onWindowLayoutChangeListenerAdded(@NonNull Context context);
+    void onWindowLayoutChangeListenerAdded(@NonNull Activity activity);
 
     /**
      * Notifies extension that a listener for display feature layout changes was removed for the
-     * given activity or window context.
+     * given activity context.
      */
-    void onWindowLayoutChangeListenerRemoved(@NonNull Context context);
+    void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity);
 
     /**
      * Notifies the extension that a device state change listener was updated. Should notify the
@@ -70,7 +70,7 @@
         /**
          * Called by extension when the feature layout inside the window changes.
          */
-        void onWindowLayoutChanged(@NonNull Context context,
+        void onWindowLayoutChanged(@NonNull Activity activity,
                 @NonNull WindowLayoutInfo newLayout);
     }
 }
diff --git a/window/window/src/main/java/androidx/window/ExtensionTranslatingCallback.java b/window/window/src/main/java/androidx/window/ExtensionTranslatingCallback.java
index 7a58574..1e1570c 100644
--- a/window/window/src/main/java/androidx/window/ExtensionTranslatingCallback.java
+++ b/window/window/src/main/java/androidx/window/ExtensionTranslatingCallback.java
@@ -19,7 +19,7 @@
 import static androidx.window.ExtensionInterfaceCompat.ExtensionCallbackInterface;
 import static androidx.window.extensions.ExtensionInterface.ExtensionCallback;
 
-import android.content.Context;
+import android.app.Activity;
 
 import androidx.annotation.NonNull;
 import androidx.window.extensions.ExtensionDeviceState;
@@ -45,8 +45,8 @@
     }
 
     @Override
-    public void onWindowLayoutChanged(@NonNull Context context,
+    public void onWindowLayoutChanged(@NonNull Activity activity,
             @NonNull ExtensionWindowLayoutInfo newLayout) {
-        mCallback.onWindowLayoutChanged(context, mAdapter.translate(context, newLayout));
+        mCallback.onWindowLayoutChanged(activity, mAdapter.translate(activity, newLayout));
     }
 }
diff --git a/window/window/src/main/java/androidx/window/ExtensionWindowBackend.java b/window/window/src/main/java/androidx/window/ExtensionWindowBackend.java
index 1f35772..44e0c47 100644
--- a/window/window/src/main/java/androidx/window/ExtensionWindowBackend.java
+++ b/window/window/src/main/java/androidx/window/ExtensionWindowBackend.java
@@ -17,7 +17,6 @@
 package androidx.window;
 
 import static androidx.window.ExtensionCompat.DEBUG;
-import static androidx.window.WindowManager.getActivityFromContext;
 
 import android.annotation.SuppressLint;
 import android.app.Activity;
@@ -74,7 +73,7 @@
     /** Window layouts that were last reported through callbacks, used to filter out duplicates. */
     @GuardedBy("sLock")
     @VisibleForTesting
-    final Map<Context, WindowLayoutInfo> mLastReportedWindowLayouts = new WeakHashMap<>();
+    final Map<Activity, WindowLayoutInfo> mLastReportedWindowLayouts = new WeakHashMap<>();
 
     private static final String TAG = "WindowServer";
 
@@ -110,7 +109,7 @@
     }
 
     @Override
-    public void registerLayoutChangeCallback(@NonNull Context context,
+    public void registerLayoutChangeCallback(@NonNull Activity activity,
             @NonNull Executor executor, @NonNull Consumer<WindowLayoutInfo> callback) {
         synchronized (sLock) {
             if (mWindowExtension == null) {
@@ -119,28 +118,27 @@
                 }
                 return;
             }
-            assertActivityContext(context);
 
-            // Check if the context was already registered, in case we need to report tracking of a
-            // new context to the extension.
-            boolean isContextRegistered = isContextRegistered(context);
+            // Check if the activity was already registered, in case we need to report tracking of a
+            // new activity to the extension.
+            boolean isActivityRegistered = isActivityRegistered(activity);
 
             WindowLayoutChangeCallbackWrapper callbackWrapper =
-                    new WindowLayoutChangeCallbackWrapper(context, executor, callback);
+                    new WindowLayoutChangeCallbackWrapper(activity, executor, callback);
             mWindowLayoutChangeCallbacks.add(callbackWrapper);
-            if (!isContextRegistered) {
-                mWindowExtension.onWindowLayoutChangeListenerAdded(context);
+            if (!isActivityRegistered) {
+                mWindowExtension.onWindowLayoutChangeListenerAdded(activity);
             }
-            WindowLayoutInfo lastReportedValue = mLastReportedWindowLayouts.get(context);
+            WindowLayoutInfo lastReportedValue = mLastReportedWindowLayouts.get(activity);
             if (lastReportedValue != null) {
                 callbackWrapper.accept(lastReportedValue);
             }
         }
     }
 
-    private boolean isContextRegistered(@NonNull Context context) {
+    private boolean isActivityRegistered(@NonNull Activity activity) {
         for (WindowLayoutChangeCallbackWrapper callbackWrapper : mWindowLayoutChangeCallbacks) {
-            if (callbackWrapper.mContext.equals(context)) {
+            if (callbackWrapper.mActivity.equals(activity)) {
                 return true;
             }
         }
@@ -169,25 +167,25 @@
             // Remove the items from the list and notify extension if needed.
             mWindowLayoutChangeCallbacks.removeAll(itemsToRemove);
             for (WindowLayoutChangeCallbackWrapper callbackWrapper : itemsToRemove) {
-                callbackRemovedForContext(callbackWrapper.mContext);
+                callbackRemovedForActivity(callbackWrapper.mActivity);
             }
         }
     }
 
     /**
-     * Checks if there are no more registered callbacks left for the context and inform extension if
-     * needed.
+     * Checks if there are no more registered callbacks left for the activity and inform
+     * extension if needed.
      */
     @GuardedBy("sLock")
-    private void callbackRemovedForContext(Context context) {
+    private void callbackRemovedForActivity(Activity activity) {
         for (WindowLayoutChangeCallbackWrapper callbackWrapper : mWindowLayoutChangeCallbacks) {
-            if (callbackWrapper.mContext.equals(context)) {
+            if (callbackWrapper.mActivity.equals(activity)) {
                 // Found a registered callback for token.
                 return;
             }
         }
-        // No registered callbacks left for context - report to extension.
-        mWindowExtension.onWindowLayoutChangeListenerRemoved(context);
+        // No registered callbacks left for the activity - report to extension.
+        mWindowExtension.onWindowLayoutChangeListenerRemoved(activity);
     }
 
     @Override
@@ -262,10 +260,10 @@
 
         @Override
         @SuppressLint("SyntheticAccessor")
-        public void onWindowLayoutChanged(@NonNull Context context,
+        public void onWindowLayoutChanged(@NonNull Activity activity,
                 @NonNull WindowLayoutInfo newLayout) {
             synchronized (sLock) {
-                WindowLayoutInfo lastReportedValue = mLastReportedWindowLayouts.get(context);
+                WindowLayoutInfo lastReportedValue = mLastReportedWindowLayouts.get(activity);
                 if (newLayout.equals(lastReportedValue)) {
                     // Skipping, value already reported
                     if (DEBUG) {
@@ -273,11 +271,11 @@
                     }
                     return;
                 }
-                mLastReportedWindowLayouts.put(context, newLayout);
+                mLastReportedWindowLayouts.put(activity, newLayout);
             }
 
             for (WindowLayoutChangeCallbackWrapper callbackWrapper : mWindowLayoutChangeCallbacks) {
-                if (!callbackWrapper.mContext.equals(context)) {
+                if (!callbackWrapper.mActivity.equals(activity)) {
                     continue;
                 }
 
@@ -286,27 +284,18 @@
         }
     }
 
-    private Activity assertActivityContext(Context context) {
-        Activity activity = getActivityFromContext(context);
-        if (activity == null) {
-            throw new IllegalArgumentException("Used non-visual Context with WindowManager. "
-                    + "Please use an Activity or a ContextWrapper around an Activity instead.");
-        }
-        return activity;
-    }
-
     /**
      * Wrapper around {@link Consumer<WindowLayoutInfo>} that also includes the {@link Executor}
-     * on which the callback should run and the visual context.
+     * on which the callback should run and the {@link Activity}.
      */
     private static class WindowLayoutChangeCallbackWrapper {
         final Executor mExecutor;
         final Consumer<WindowLayoutInfo> mCallback;
-        final Context mContext;
+        final Activity mActivity;
 
-        WindowLayoutChangeCallbackWrapper(@NonNull Context context, @NonNull Executor executor,
+        WindowLayoutChangeCallbackWrapper(@NonNull Activity activity, @NonNull Executor executor,
                 @NonNull Consumer<WindowLayoutInfo> callback) {
-            mContext = context;
+            mActivity = activity;
             mExecutor = executor;
             mCallback = callback;
         }
@@ -345,28 +334,44 @@
         try {
             if (isExtensionVersionSupported(ExtensionCompat.getExtensionVersion())) {
                 impl = new ExtensionCompat(context);
-            } else if (isExtensionVersionSupported(SidecarCompat.getSidecarVersion())) {
-                impl = new SidecarCompat(context);
+                if (!impl.validateExtensionInterface()) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Loaded extension doesn't match the interface version");
+                    }
+                    impl = null;
+                }
             }
         } catch (Throwable t) {
             if (DEBUG) {
                 Log.d(TAG, "Failed to load extension: " + t);
             }
-            return null;
+            impl = null;
+        }
+
+        if (impl == null) {
+            // Falling back to Sidecar
+            try {
+                if (isExtensionVersionSupported(SidecarCompat.getSidecarVersion())) {
+                    impl = new SidecarCompat(context);
+                    if (!impl.validateExtensionInterface()) {
+                        if (DEBUG) {
+                            Log.d(TAG, "Loaded Sidecar doesn't match the interface version");
+                        }
+                        impl = null;
+                    }
+                }
+            } catch (Throwable t) {
+                if (DEBUG) {
+                    Log.d(TAG, "Failed to load sidecar: " + t);
+                }
+                impl = null;
+            }
         }
 
         if (impl == null) {
             if (DEBUG) {
-                Log.d(TAG, "No supported extension found");
+                Log.d(TAG, "No supported extension or sidecar found");
             }
-            return null;
-        }
-
-        if (!impl.validateExtensionInterface()) {
-            if (DEBUG) {
-                Log.d(TAG, "Loaded extension doesn't match the interface version");
-            }
-            return null;
         }
 
         return impl;
@@ -378,8 +383,15 @@
      */
     @VisibleForTesting
     static boolean isExtensionVersionSupported(@Nullable Version extensionVersion) {
-        return extensionVersion != null
-                && Version.CURRENT.getMajor() >= extensionVersion.getMajor();
+        if (extensionVersion == null) {
+            return false;
+        }
+        if (extensionVersion.getMajor() == 1) {
+            // Disable androidx.window.extensions support in release builds of the library until the
+            // extensions API is finalized.
+            return DEBUG;
+        }
+        return Version.CURRENT.getMajor() >= extensionVersion.getMajor();
     }
 
     /**
diff --git a/window/window/src/main/java/androidx/window/SidecarCompat.java b/window/window/src/main/java/androidx/window/SidecarCompat.java
index ec58604..7de2b77 100644
--- a/window/window/src/main/java/androidx/window/SidecarCompat.java
+++ b/window/window/src/main/java/androidx/window/SidecarCompat.java
@@ -18,7 +18,6 @@
 
 import static androidx.window.ExtensionCompat.DEBUG;
 import static androidx.window.Version.VERSION_0_1;
-import static androidx.window.WindowManager.getActivityFromContext;
 
 import android.annotation.SuppressLint;
 import android.app.Activity;
@@ -50,7 +49,7 @@
 
     // Map of active listeners registered with #onWindowLayoutChangeListenerAdded() and not yet
     // removed by #onWindowLayoutChangeListenerRemoved().
-    protected final SimpleArrayMap<IBinder, Context> mWindowListenerRegisteredContexts =
+    protected final SimpleArrayMap<IBinder, Activity> mWindowListenerRegisteredContexts =
             new SimpleArrayMap<>();
 
     private ExtensionCallbackInterface mExtensionCallback;
@@ -76,7 +75,7 @@
             }
 
             @Override
-            public void onWindowLayoutChanged(@NonNull Context context,
+            public void onWindowLayoutChanged(@NonNull Activity activity,
                     @NonNull WindowLayoutInfo newLayout) {
 
             }
@@ -99,20 +98,14 @@
             @SuppressLint("SyntheticAccessor")
             public void onWindowLayoutChanged(@NonNull IBinder windowToken,
                     @NonNull SidecarWindowLayoutInfo newLayout) {
-                Context context = mWindowListenerRegisteredContexts.get(windowToken);
-                if (context == null) {
-                    Log.w(TAG, "Unable to resolve context from window token. Missing a call"
+                Activity activity = mWindowListenerRegisteredContexts.get(windowToken);
+                if (activity == null) {
+                    Log.w(TAG, "Unable to resolve activity from window token. Missing a call"
                             + "to #onWindowLayoutChangeListenerAdded()?");
                     return;
                 }
 
-                Activity activity = getActivityFromContext(context);
-                if (activity == null) {
-                    throw new IllegalArgumentException(
-                            "Used non-Activity Context with WindowManager. Please use an Activity "
-                                    + "or a ContextWrapper around an Activity instead.");
-                }
-                extensionCallback.onWindowLayoutChanged(context,
+                extensionCallback.onWindowLayoutChanged(activity,
                         mSidecarAdapter.translate(activity, newLayout));
             }
         });
@@ -128,8 +121,7 @@
     }
 
     @Override
-    public void onWindowLayoutChangeListenerAdded(@NonNull Context context) {
-        Activity activity = assertActivityContext(context);
+    public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
         IBinder windowToken = getActivityWindowToken(activity);
 
         if (windowToken != null) {
@@ -150,8 +142,7 @@
     }
 
     @Override
-    public void onWindowLayoutChangeListenerRemoved(@NonNull Context context) {
-        Activity activity = assertActivityContext(context);
+    public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
         IBinder windowToken = getActivityWindowToken(activity);
 
         mSidecar.onWindowLayoutChangeListenerRemoved(windowToken);
@@ -271,15 +262,6 @@
         }
     }
 
-    private Activity assertActivityContext(Context context) {
-        Activity activity = getActivityFromContext(context);
-        if (activity == null) {
-            throw new IllegalArgumentException("Used non-visual Context with WindowManager. "
-                    + "Please use an Activity or a ContextWrapper around an Activity instead.");
-        }
-        return activity;
-    }
-
     @Nullable
     private IBinder getActivityWindowToken(Activity activity) {
         return activity.getWindow() != null ? activity.getWindow().getAttributes().token : null;
diff --git a/window/window/src/main/java/androidx/window/WindowBackend.java b/window/window/src/main/java/androidx/window/WindowBackend.java
index 7482dc3..f988c82 100644
--- a/window/window/src/main/java/androidx/window/WindowBackend.java
+++ b/window/window/src/main/java/androidx/window/WindowBackend.java
@@ -16,7 +16,7 @@
 
 package androidx.window;
 
-import android.content.Context;
+import android.app.Activity;
 
 import androidx.annotation.NonNull;
 import androidx.core.util.Consumer;
@@ -30,14 +30,14 @@
 public interface WindowBackend {
 
     /**
-     * Registers a callback for layout changes of the window of the current visual {@link Context}.
+     * Registers a callback for layout changes of the window for the supplied {@link Activity}.
      * Must be called only after the it is attached to the window.
      */
-    void registerLayoutChangeCallback(@NonNull Context context, @NonNull Executor executor,
+    void registerLayoutChangeCallback(@NonNull Activity activity, @NonNull Executor executor,
             @NonNull Consumer<WindowLayoutInfo> callback);
 
     /**
-     * Unregisters a callback for window layout changes of the {@link Context} window.
+     * Unregisters a callback for window layout changes of the {@link Activity} window.
      */
     void unregisterLayoutChangeCallback(@NonNull Consumer<WindowLayoutInfo> callback);
 
diff --git a/window/window/src/main/java/androidx/window/WindowManager.java b/window/window/src/main/java/androidx/window/WindowManager.java
index 76c3633..6d83906 100644
--- a/window/window/src/main/java/androidx/window/WindowManager.java
+++ b/window/window/src/main/java/androidx/window/WindowManager.java
@@ -35,11 +35,12 @@
  */
 public final class WindowManager {
     /**
-     * Visual context that was registered with this instance of {@link WindowManager} at creation.
+     * Activity that was registered with this instance of {@link WindowManager} at creation.
      * This is used to find the token identifier of the window when requesting layout information
-     * from the {@link ExtensionInterface}.
+     * from the {@link androidx.window.sidecar.SidecarInterface} or is passed directly to the
+     * {@link ExtensionInterface}.
      */
-    private Context mContext;
+    private Activity mActivity;
     /**
      * The backend that supplies the information through this class.
      */
@@ -66,12 +67,13 @@
      *                      Pass a custom {@link WindowBackend} implementation for testing.
      */
     public WindowManager(@NonNull Context context, @NonNull WindowBackend windowBackend) {
-        if (getActivityFromContext(context) == null) {
+        Activity activity = getActivityFromContext(context);
+        if (activity == null) {
             throw new IllegalArgumentException("Used non-visual Context to obtain an instance of "
                     + "WindowManager. Please use an Activity or a ContextWrapper around one "
                     + "instead.");
         }
-        mContext = context;
+        mActivity = activity;
         mWindowBackend = windowBackend;
     }
 
@@ -82,7 +84,7 @@
      */
     public void registerLayoutChangeCallback(@NonNull Executor executor,
             @NonNull Consumer<WindowLayoutInfo> callback) {
-        mWindowBackend.registerLayoutChangeCallback(mContext, executor, callback);
+        mWindowBackend.registerLayoutChangeCallback(mActivity, executor, callback);
     }
 
     /**
@@ -124,8 +126,7 @@
      */
     @NonNull
     public WindowMetrics getCurrentWindowMetrics() {
-        Activity activity = getActivityFromContext(mContext);
-        Rect currentBounds = WindowBoundsHelper.getInstance().computeCurrentWindowBounds(activity);
+        Rect currentBounds = WindowBoundsHelper.getInstance().computeCurrentWindowBounds(mActivity);
         return new WindowMetrics(currentBounds);
     }
 
@@ -153,8 +154,7 @@
      */
     @NonNull
     public WindowMetrics getMaximumWindowMetrics() {
-        Activity activity = getActivityFromContext(mContext);
-        Rect maxBounds = WindowBoundsHelper.getInstance().computeMaximumWindowBounds(activity);
+        Rect maxBounds = WindowBoundsHelper.getInstance().computeMaximumWindowBounds(mActivity);
         return new WindowMetrics(maxBounds);
     }
 
@@ -163,7 +163,7 @@
      * @return Base {@link Activity} context or {@code null} if not available.
      */
     @Nullable
-    static Activity getActivityFromContext(Context context) {
+    private static Activity getActivityFromContext(Context context) {
         while (context instanceof ContextWrapper) {
             if (context instanceof Activity) {
                 return (Activity) context;
@@ -172,21 +172,4 @@
         }
         return null;
     }
-
-    /**
-     * Unwraps the hierarchy of {@link ContextWrapper}-s until {@link Activity} is reached.
-     * @return Base {@link Activity} context or throws {@link IllegalArgumentException} if not
-     * available.
-     */
-    @NonNull
-    static Activity assertActivityFromContext(Context context) throws IllegalArgumentException {
-        while (context instanceof ContextWrapper) {
-            if (context instanceof Activity) {
-                return (Activity) context;
-            }
-            context = ((ContextWrapper) context).getBaseContext();
-        }
-        throw new IllegalArgumentException("Used non-visual Context with WindowManager. "
-                + "Please use an Activity or a ContextWrapper around an Activity instead.");
-    }
 }