[go: nahoru, domu]

Merge "MediaRouter: Fix dynamic group bug" into androidx-main
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTestBase.java
index 2369c2c..be85f72 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTestBase.java
@@ -116,6 +116,7 @@
     }
 
 // @exportToFramework:startStrip()
+
     @Test
     public void testSetSchema_dataClass() throws Exception {
         mDb1.setSchema(
@@ -124,6 +125,7 @@
 // @exportToFramework:endStrip()
 
 // @exportToFramework:startStrip()
+
     @Test
     public void testGetSchema() throws Exception {
         AppSearchSchema emailSchema1 = new AppSearchSchema.Builder("Email1")
@@ -192,6 +194,7 @@
     }
 
 // @exportToFramework:startStrip()
+
     @Test
     public void testPutDocuments_dataClass() throws Exception {
         // Schema registration
@@ -452,6 +455,7 @@
     }
 
 // @exportToFramework:startStrip()
+
     @Test
     public void testGetDocuments_dataClass() throws Exception {
         // Schema registration
@@ -557,6 +561,60 @@
     }
 
     @Test
+    public void testQuery_relevanceScoring() throws Exception {
+        // Schema registration
+        mDb1.setSchema(
+                new SetSchemaRequest.Builder()
+                        .addSchema(AppSearchEmail.SCHEMA)
+                        .build()).get();
+
+        // Index two documents
+        AppSearchEmail email1 =
+                new AppSearchEmail.Builder("uri1")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("from@example.com")
+                        .setTo("to1@example.com", "to2@example.com")
+                        .setSubject("Mary had a little lamb")
+                        .setBody("A little lamb, little lamb")
+                        .build();
+        AppSearchEmail email2 =
+                new AppSearchEmail.Builder("uri2")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("from@example.com")
+                        .setTo("to1@example.com", "to2@example.com")
+                        .setSubject("I'm a little teapot")
+                        .setBody("short and stout. Here is my handle, here is my spout.")
+                        .build();
+        checkIsBatchResultSuccess(mDb1.putDocuments(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocument(email1, email2).build()));
+
+        // Query for "little". It should match both emails.
+        SearchResults searchResults = mDb1.query("little", new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE)
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+
+        // The email1 should be ranked higher because 'little' appears three times in email1 and
+        // only once in email2.
+        assertThat(documents).containsExactly(email1, email2).inOrder();
+
+        // Query for "little OR stout". It should match both emails.
+        searchResults = mDb1.query("little OR stout", new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE)
+                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+
+        // The email2 should be ranked higher because 'little' appears once and "stout", which is a
+        // rarer term, appears once. email1 only has the three 'little' appearances.
+        assertThat(documents).containsExactly(email2, email1).inOrder();
+    }
+
+    @Test
     public void testQuery_typeFilter() throws Exception {
         // Schema registration
         AppSearchSchema genericSchema = new AppSearchSchema.Builder("Generic")
@@ -692,10 +750,23 @@
         mDb1.setSchema(
                 new SetSchemaRequest.Builder()
                         .addSchema(AppSearchEmail.SCHEMA)
-                        .build()).get();
+                        .addSchema(new AppSearchSchema.Builder("Note")
+                                .addProperty(new PropertyConfig.Builder("title")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .addProperty(new PropertyConfig.Builder("body")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .build()).build()).get();
 
         // Index two documents
-        AppSearchEmail email1 =
+        AppSearchEmail email =
                 new AppSearchEmail.Builder("uri1")
                         .setNamespace("namespace")
                         .setCreationTimestampMillis(1000)
@@ -704,56 +775,64 @@
                         .setSubject("testPut example")
                         .setBody("This is the body of the testPut email")
                         .build();
-        AppSearchEmail email2 =
-                new AppSearchEmail.Builder("uri2")
+        GenericDocument note =
+                new GenericDocument.Builder<>("uri2", "Note")
                         .setNamespace("namespace")
                         .setCreationTimestampMillis(1000)
-                        .setFrom("from@example.com")
-                        .setTo("to1@example.com", "to2@example.com")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
         checkIsBatchResultSuccess(mDb1.putDocuments(
                 new PutDocumentsRequest.Builder()
-                        .addGenericDocument(email1, email2).build()));
+                        .addGenericDocument(email, note).build()));
 
-        // Query with type property paths {"Email", ["subject", "to"]}
+        // Query with type property paths {"Email", ["body", "to"]}
         SearchResults searchResults = mDb1.query("body", new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .addProjection(AppSearchEmail.SCHEMA_TYPE, "subject", "to")
+                .addProjection(AppSearchEmail.SCHEMA_TYPE, "body", "to")
                 .build());
         List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
 
-        // The two email documents should have been returned with only the "subject" and "to"
-        // properties.
-        AppSearchEmail expected1 =
-                new AppSearchEmail.Builder("uri2")
-                        .setNamespace("namespace")
-                        .setCreationTimestampMillis(1000)
-                        .setTo("to1@example.com", "to2@example.com")
-                        .setSubject("testPut example")
-                        .build();
-        AppSearchEmail expected2 =
+        // The email document should have been returned with only the "body" and "to"
+        // properties. The note document should have been returned with all of its properties.
+        AppSearchEmail expectedEmail =
                 new AppSearchEmail.Builder("uri1")
                         .setNamespace("namespace")
                         .setCreationTimestampMillis(1000)
                         .setTo("to1@example.com", "to2@example.com")
-                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
                         .build();
-        assertThat(documents).containsExactly(expected1, expected2);
+        GenericDocument expectedNote =
+                new GenericDocument.Builder<>("uri2", "Note")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
+        assertThat(documents).containsExactly(expectedNote, expectedEmail);
     }
 
-    // TODO(b/175039682) Add test cases for wildcard projection once go/oag/1534646 is submitted.
     @Test
     public void testQuery_projectionEmpty() throws Exception {
         // Schema registration
         mDb1.setSchema(
                 new SetSchemaRequest.Builder()
                         .addSchema(AppSearchEmail.SCHEMA)
-                        .build()).get();
+                        .addSchema(new AppSearchSchema.Builder("Note")
+                                .addProperty(new PropertyConfig.Builder("title")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .addProperty(new PropertyConfig.Builder("body")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .build()).build()).get();
 
         // Index two documents
-        AppSearchEmail email1 =
+        AppSearchEmail email =
                 new AppSearchEmail.Builder("uri1")
                         .setNamespace("namespace")
                         .setCreationTimestampMillis(1000)
@@ -762,18 +841,15 @@
                         .setSubject("testPut example")
                         .setBody("This is the body of the testPut email")
                         .build();
-        AppSearchEmail email2 =
-                new AppSearchEmail.Builder("uri2")
+        GenericDocument note =
+                new GenericDocument.Builder<>("uri2", "Note")
                         .setNamespace("namespace")
                         .setCreationTimestampMillis(1000)
-                        .setFrom("from@example.com")
-                        .setTo("to1@example.com", "to2@example.com")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
         checkIsBatchResultSuccess(mDb1.putDocuments(
                 new PutDocumentsRequest.Builder()
-                        .addGenericDocument(email1, email2).build()));
+                        .addGenericDocument(email, note).build()));
 
         // Query with type property paths {"Email", []}
         SearchResults searchResults = mDb1.query("body", new SearchSpec.Builder()
@@ -782,18 +858,20 @@
                 .build());
         List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
 
-        // The two email documents should have been returned without any properties.
-        AppSearchEmail expected1 =
-                new AppSearchEmail.Builder("uri2")
-                        .setNamespace("namespace")
-                        .setCreationTimestampMillis(1000)
-                        .build();
-        AppSearchEmail expected2 =
+        // The email document should have been returned without any properties. The note document
+        // should have been returned with all of its properties.
+        AppSearchEmail expectedEmail =
                 new AppSearchEmail.Builder("uri1")
                         .setNamespace("namespace")
                         .setCreationTimestampMillis(1000)
                         .build();
-        assertThat(documents).containsExactly(expected1, expected2);
+        GenericDocument expectedNote =
+                new GenericDocument.Builder<>("uri2", "Note")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
+        assertThat(documents).containsExactly(expectedNote, expectedEmail);
     }
 
     @Test
@@ -802,10 +880,23 @@
         mDb1.setSchema(
                 new SetSchemaRequest.Builder()
                         .addSchema(AppSearchEmail.SCHEMA)
-                        .build()).get();
+                        .addSchema(new AppSearchSchema.Builder("Note")
+                                .addProperty(new PropertyConfig.Builder("title")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .addProperty(new PropertyConfig.Builder("body")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .build()).build()).get();
 
         // Index two documents
-        AppSearchEmail email1 =
+        AppSearchEmail email =
                 new AppSearchEmail.Builder("uri1")
                         .setNamespace("namespace")
                         .setCreationTimestampMillis(1000)
@@ -814,44 +905,232 @@
                         .setSubject("testPut example")
                         .setBody("This is the body of the testPut email")
                         .build();
-        AppSearchEmail email2 =
-                new AppSearchEmail.Builder("uri2")
+        GenericDocument note =
+                new GenericDocument.Builder<>("uri2", "Note")
                         .setNamespace("namespace")
                         .setCreationTimestampMillis(1000)
-                        .setFrom("from@example.com")
-                        .setTo("to1@example.com", "to2@example.com")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
         checkIsBatchResultSuccess(mDb1.putDocuments(
                 new PutDocumentsRequest.Builder()
-                        .addGenericDocument(email1, email2).build()));
+                        .addGenericDocument(email, note).build()));
 
-        // Query with type property paths {"NonExistentType", []}, {"Email", ["subject", "to"]}
+        // Query with type property paths {"NonExistentType", []}, {"Email", ["body", "to"]}
         SearchResults searchResults = mDb1.query("body", new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                 .addProjection("NonExistentType", Collections.emptyList())
-                .addProjection(AppSearchEmail.SCHEMA_TYPE, "subject", "to")
+                .addProjection(AppSearchEmail.SCHEMA_TYPE, "body", "to")
                 .build());
         List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
 
-        // The two email documents should have been returned with only the "subject" and "to"
-        // properties.
-        AppSearchEmail expected1 =
-                new AppSearchEmail.Builder("uri2")
-                        .setNamespace("namespace")
-                        .setCreationTimestampMillis(1000)
-                        .setTo("to1@example.com", "to2@example.com")
-                        .setSubject("testPut example")
-                        .build();
-        AppSearchEmail expected2 =
+        // The email document should have been returned with only the "body" and "to" properties.
+        // The note document should have been returned with all of its properties.
+        AppSearchEmail expectedEmail =
                 new AppSearchEmail.Builder("uri1")
                         .setNamespace("namespace")
                         .setCreationTimestampMillis(1000)
                         .setTo("to1@example.com", "to2@example.com")
-                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
                         .build();
-        assertThat(documents).containsExactly(expected1, expected2);
+        GenericDocument expectedNote =
+                new GenericDocument.Builder<>("uri2", "Note")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
+        assertThat(documents).containsExactly(expectedNote, expectedEmail);
+    }
+
+    @Test
+    public void testQuery_wildcardProjection() throws Exception {
+        // Schema registration
+        mDb1.setSchema(
+                new SetSchemaRequest.Builder()
+                        .addSchema(AppSearchEmail.SCHEMA)
+                        .addSchema(new AppSearchSchema.Builder("Note")
+                                .addProperty(new PropertyConfig.Builder("title")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .addProperty(new PropertyConfig.Builder("body")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .build()).build()).get();
+
+        // Index two documents
+        AppSearchEmail email =
+                new AppSearchEmail.Builder("uri1")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("from@example.com")
+                        .setTo("to1@example.com", "to2@example.com")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        GenericDocument note =
+                new GenericDocument.Builder<>("uri2", "Note")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
+        checkIsBatchResultSuccess(mDb1.putDocuments(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocument(email, note).build()));
+
+        // Query with type property paths {"*", ["body", "to"]}
+        SearchResults searchResults = mDb1.query("body", new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addProjection(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD, "body", "to")
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+
+        // The email document should have been returned with only the "body" and "to"
+        // properties. The note document should have been returned with only the "body" property.
+        AppSearchEmail expectedEmail =
+                new AppSearchEmail.Builder("uri1")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setTo("to1@example.com", "to2@example.com")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        GenericDocument expectedNote =
+                new GenericDocument.Builder<>("uri2", "Note")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("body", "Note body").build();
+        assertThat(documents).containsExactly(expectedNote, expectedEmail);
+    }
+
+    @Test
+    public void testQuery_wildcardProjectionEmpty() throws Exception {
+        // Schema registration
+        mDb1.setSchema(
+                new SetSchemaRequest.Builder()
+                        .addSchema(AppSearchEmail.SCHEMA)
+                        .addSchema(new AppSearchSchema.Builder("Note")
+                                .addProperty(new PropertyConfig.Builder("title")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .addProperty(new PropertyConfig.Builder("body")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .build()).build()).get();
+
+        // Index two documents
+        AppSearchEmail email =
+                new AppSearchEmail.Builder("uri1")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("from@example.com")
+                        .setTo("to1@example.com", "to2@example.com")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        GenericDocument note =
+                new GenericDocument.Builder<>("uri2", "Note")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
+        checkIsBatchResultSuccess(mDb1.putDocuments(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocument(email, note).build()));
+
+        // Query with type property paths {"*", []}
+        SearchResults searchResults = mDb1.query("body", new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addProjection(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD, Collections.emptyList())
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+
+        // The email and note documents should have been returned without any properties.
+        AppSearchEmail expectedEmail =
+                new AppSearchEmail.Builder("uri1")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .build();
+        GenericDocument expectedNote =
+                new GenericDocument.Builder<>("uri2", "Note")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000).build();
+        assertThat(documents).containsExactly(expectedNote, expectedEmail);
+    }
+
+    @Test
+    public void testQuery_wildcardProjectionNonExistentType() throws Exception {
+        // Schema registration
+        mDb1.setSchema(
+                new SetSchemaRequest.Builder()
+                        .addSchema(AppSearchEmail.SCHEMA)
+                        .addSchema(new AppSearchSchema.Builder("Note")
+                                .addProperty(new PropertyConfig.Builder("title")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .addProperty(new PropertyConfig.Builder("body")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                                        .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                PropertyConfig.TOKENIZER_TYPE_PLAIN).build())
+                                .build()).build()).get();
+
+        // Index two documents
+        AppSearchEmail email =
+                new AppSearchEmail.Builder("uri1")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("from@example.com")
+                        .setTo("to1@example.com", "to2@example.com")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        GenericDocument note =
+                new GenericDocument.Builder<>("uri2", "Note")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
+        checkIsBatchResultSuccess(mDb1.putDocuments(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocument(email, note).build()));
+
+        // Query with type property paths {"NonExistentType", []}, {"*", ["body", "to"]}
+        SearchResults searchResults = mDb1.query("body", new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addProjection("NonExistentType", Collections.emptyList())
+                .addProjection(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD, "body", "to")
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+
+        // The email document should have been returned with only the "body" and "to"
+        // properties. The note document should have been returned with only the "body" property.
+        AppSearchEmail expectedEmail =
+                new AppSearchEmail.Builder("uri1")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setTo("to1@example.com", "to2@example.com")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        GenericDocument expectedNote =
+                new GenericDocument.Builder<>("uri2", "Note")
+                        .setNamespace("namespace")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("body", "Note body").build();
+        assertThat(documents).containsExactly(expectedNote, expectedEmail);
     }
 
     @Test
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
index 51eb7f5..e97d137 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
@@ -944,10 +944,13 @@
             // Qualify the given schema types
             for (TypePropertyMask typePropertyMask :
                     resultSpecBuilder.getTypePropertyMasksList()) {
-                String qualifiedType = prefix + typePropertyMask.getSchemaType();
-                if (existingSchemaTypes.contains(qualifiedType)) {
+                String unprefixedType = typePropertyMask.getSchemaType();
+                boolean isWildcard =
+                        unprefixedType.equals(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD);
+                String prefixedType = isWildcard ? unprefixedType : prefix + unprefixedType;
+                if (isWildcard || existingSchemaTypes.contains(prefixedType)) {
                     prefixedTypePropertyMasks.add(
-                            typePropertyMask.toBuilder().setSchemaType(qualifiedType).build());
+                            typePropertyMask.toBuilder().setSchemaType(prefixedType).build());
                 }
             }
         }
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/JankMetricValidation.kt b/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/FrameTimingMetricValidation.kt
similarity index 95%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/JankMetricValidation.kt
rename to benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/FrameTimingMetricValidation.kt
index 8d2e9a2..e88aea4 100644
--- a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/JankMetricValidation.kt
+++ b/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/FrameTimingMetricValidation.kt
@@ -18,7 +18,7 @@
 
 import android.content.Intent
 import androidx.benchmark.macro.CompilationMode
-import androidx.benchmark.macro.JankMetric
+import androidx.benchmark.macro.FrameTimingMetric
 import androidx.benchmark.macro.MacrobenchmarkConfig
 import androidx.benchmark.macro.MacrobenchmarkRule
 import androidx.test.filters.LargeTest
@@ -36,7 +36,7 @@
 @LargeTest
 @SdkSuppress(minSdkVersion = 29)
 @RunWith(Parameterized::class)
-class JankMetricValidation(
+class FrameTimingMetricValidation(
     private val compilationMode: CompilationMode
 ) {
     @get:Rule
@@ -54,7 +54,7 @@
     fun start() {
         val config = MacrobenchmarkConfig(
             packageName = PACKAGE_NAME,
-            metrics = listOf(JankMetric()),
+            metrics = listOf(FrameTimingMetric()),
             compilationMode = compilationMode,
             iterations = 10
         )
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/StartupUtils.kt b/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/StartupUtils.kt
index e8c152e..bc58b13 100644
--- a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/StartupUtils.kt
+++ b/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/StartupUtils.kt
@@ -48,4 +48,4 @@
     intent.setPackage(TARGET_PACKAGE)
     setupIntent(intent)
     launchIntentAndWait(intent)
-}
\ No newline at end of file
+}
diff --git a/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index 2d211f9..50b0812 100644
--- a/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -100,10 +100,20 @@
 
 data class MacrobenchmarkConfig(
     val packageName: String,
-    val metrics: List<Metric>,
+    var metrics: List<Metric>,
     val compilationMode: CompilationMode = CompilationMode.SpeedProfile(),
     val iterations: Int
-)
+) {
+    init {
+        val metricSet = metrics.toSet()
+        val hasStartupMetric = metricSet.any { it is StartupTimingMetric }
+        if (hasStartupMetric) {
+            val metrics = metrics.toMutableList()
+            metrics += PerfettoMetric()
+            this.metrics = metrics.toList()
+        }
+    }
+}
 
 /**
  * macrobenchmark test entrypoint, which doesn't depend on JUnit.
@@ -141,24 +151,23 @@
         val metricResults = List(config.iterations) { iteration ->
             setupBlock(scope, isFirstRun)
             isFirstRun = false
-            try {
-                perfettoCollector.start()
-                config.metrics.forEach {
-                    it.start()
+            perfettoCollector.captureTrace(uniqueName, iteration) { tracePath ->
+                try {
+                    config.metrics.forEach {
+                        it.start()
+                    }
+                    measureBlock(scope)
+                } finally {
+                    config.metrics.forEach {
+                        it.stop()
+                    }
                 }
-                measureBlock(scope)
-            } finally {
-                config.metrics.forEach {
-                    it.stop()
-                }
-                perfettoCollector.stop(uniqueName, iteration)
+                config.metrics
+                    // capture list of Map<String,Long> per metric
+                    .map { it.getMetrics(config.packageName, tracePath) }
+                    // merge into one map
+                    .reduce { sum, element -> sum + element }
             }
-
-            config.metrics
-                // capture list of Map<String,Long> per metric
-                .map { it.getMetrics(config.packageName) }
-                // merge into one map
-                .reduce { sum, element -> sum + element }
         }.mergeToMetricResults()
 
         InstrumentationResults.instrumentationReport {
diff --git a/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt b/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt
index aee2877..70aa835 100644
--- a/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt
+++ b/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt
@@ -16,6 +16,9 @@
 
 package androidx.benchmark.macro
 
+import android.util.Log
+import androidx.benchmark.perfetto.PerfettoResultsParser.parseResult
+import androidx.benchmark.perfetto.PerfettoTraceParser
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import com.android.helpers.CpuUsageHelper
@@ -31,14 +34,13 @@
     abstract fun start()
 
     abstract fun stop()
-
     /**
      * After stopping, collect metrics
      *
      * TODO: takes package for package level filtering, but probably want a
      *  general config object coming into [start].
      */
-    abstract fun getMetrics(packageName: String): Map<String, Long>
+    abstract fun getMetrics(packageName: String, tracePath: String): Map<String, Long>
 }
 
 class StartupTimingMetric : Metric() {
@@ -56,7 +58,7 @@
         helper.stopCollecting()
     }
 
-    override fun getMetrics(packageName: String): Map<String, Long> {
+    override fun getMetrics(packageName: String, tracePath: String): Map<String, Long> {
         return helper.getMetrics(packageName)
     }
 }
@@ -81,12 +83,12 @@
         helper.stopCollecting()
     }
 
-    override fun getMetrics(packageName: String): Map<String, Long> {
+    override fun getMetrics(packageName: String, tracePath: String): Map<String, Long> {
         return helper.metrics
     }
 }
 
-class JankMetric : Metric() {
+class FrameTimingMetric : Metric() {
     private lateinit var packageName: String
     private val helper = JankCollectionHelper()
 
@@ -146,7 +148,7 @@
         "slow_bmp_upload" to "slowBitmapUploadFrameCount",
         "slow_issue_draw_cmds" to "slowIssueDrawCommandsFrameCount",
         "total_frames" to "totalFrameCount",
-        "janky_frames_percent" to "jankyFramePercent",
+        "janky_frames_percent" to "jankyFramePercent"
     )
 
     /**
@@ -157,10 +159,10 @@
         "frameTime90thPercentileMs",
         "frameTime95thPercentileMs",
         "frameTime99thPercentileMs",
-        "totalFrameCount",
+        "totalFrameCount"
     )
 
-    override fun getMetrics(packageName: String): Map<String, Long> {
+    override fun getMetrics(packageName: String, tracePath: String): Map<String, Long> {
         return helper.metrics
             .map {
                 val prefix = "gfxinfo_${packageName}_"
@@ -181,6 +183,48 @@
 }
 
 /**
+ * Only does startup metrics now. Will need to expand scope.
+ */
+internal class PerfettoMetric : Metric() {
+    private lateinit var packageName: String
+    private lateinit var device: UiDevice
+    private lateinit var parser: PerfettoTraceParser
+
+    override fun configure(config: MacrobenchmarkConfig) {
+        packageName = config.packageName
+        val instrumentation = InstrumentationRegistry.getInstrumentation()
+        device = instrumentation.device()
+        parser = PerfettoTraceParser()
+    }
+
+    override fun start() {
+        parser.copyTraceProcessorShell()
+    }
+
+    override fun stop() {
+    }
+
+    override fun getMetrics(packageName: String, tracePath: String): Map<String, Long> {
+        val path = parser.shellFile?.absolutePath
+        return if (path != null) {
+            // TODO: Construct `METRICS` based on the config.
+            val command = "$path --run-metric $METRICS $tracePath --metrics-output=json"
+            Log.d(TAG, "Executing command $command")
+            val json = device.executeShellCommand(command)
+            Log.d(TAG, "Trace Processor result \n\n $json")
+            parseResult(json, packageName)
+        } else {
+            emptyMap()
+        }
+    }
+
+    companion object {
+        private const val TAG = "PerfettoMetric"
+        private const val METRICS = "android_startup"
+    }
+}
+
+/**
  * Not public, as this needs clarified metric names
  */
 internal class TotalPssMetric : Metric() {
@@ -198,7 +242,7 @@
         helper.stopCollecting()
     }
 
-    override fun getMetrics(packageName: String): Map<String, Long> {
+    override fun getMetrics(packageName: String, tracePath: String): Map<String, Long> {
         return helper.metrics
     }
 }
diff --git a/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt b/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
index 840dee5..39d26a4 100644
--- a/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
+++ b/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
@@ -19,6 +19,7 @@
 import android.os.Build
 import android.util.Log
 import androidx.benchmark.perfetto.PerfettoCapture
+import androidx.benchmark.perfetto.PerfettoHelper
 import androidx.benchmark.perfetto.destinationPath
 import androidx.benchmark.perfetto.reportAdditionalFileToCopy
 
@@ -27,14 +28,26 @@
  */
 class PerfettoCaptureWrapper {
     private var capture: PerfettoCapture? = null
-
     init {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             capture = PerfettoCapture()
         }
     }
 
-    fun start(): Boolean {
+    fun <T> captureTrace(
+        benchmarkName: String,
+        iteration: Int,
+        block: (String) -> T
+    ): T {
+        try {
+            start()
+            return block(PerfettoHelper.getPerfettoTmpOutputFilePath())
+        } finally {
+            stop(benchmarkName, iteration)
+        }
+    }
+
+    private fun start(): Boolean {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             Log.d(TAG, "Recording perfetto trace")
             capture?.start()
@@ -42,19 +55,14 @@
         return true
     }
 
-    fun stop(benchmarkName: String, iteration: Int): Boolean {
+    private fun stop(benchmarkName: String, iteration: Int): Boolean {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             val iterString = iteration.toString().padStart(3, '0')
             val traceName = "${benchmarkName}_iter$iterString.trace"
-
             val destination = destinationPath(traceName).absolutePath
             capture?.stop(destination)
             reportAdditionalFileToCopy("perfetto_trace_$iterString", destination)
         }
         return true
     }
-
-    companion object {
-        private const val TAG = "PerfettoCollector"
-    }
 }
diff --git a/benchmark/perfetto/build.gradle b/benchmark/perfetto/build.gradle
index 8210bdd..652a512 100644
--- a/benchmark/perfetto/build.gradle
+++ b/benchmark/perfetto/build.gradle
@@ -17,6 +17,7 @@
 import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
 import androidx.build.Publish
+import androidx.build.SupportConfigKt
 
 plugins {
     id("AndroidXPlugin")
@@ -30,6 +31,12 @@
         // lower minSdkVersion to enable optional usage, based on API level.
         minSdkVersion 18
     }
+    sourceSets {
+        main.assets.srcDirs += new File(
+                SupportConfigKt.getPrebuiltsRoot(project),
+                "androidx/traceprocessor/trace_processor_shell"
+        )
+    }
 }
 
 dependencies {
@@ -45,6 +52,7 @@
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
 }
+
 androidx {
     name = "Android Benchmark - Perfetto"
     publish = Publish.NONE
diff --git a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
index 77e056a..1e95916 100644
--- a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
+++ b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
@@ -16,7 +16,6 @@
 
 package androidx.benchmark.perfetto
 
-import android.content.Context
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.test.platform.app.InstrumentationRegistry
@@ -51,16 +50,13 @@
      * TODO: provide configuration options
      */
     fun start() {
-        val context: Context = InstrumentationRegistry.getInstrumentation().context
-
+        val context = InstrumentationRegistry.getInstrumentation().context
         // Write textproto asset to external files dir, so it can be read by shell
         // TODO: use binary proto (which will also give us rooted 28 support)
         val configBytes = context.resources.openRawResource(R.raw.trace_config).readBytes()
         val textProtoFile = File(context.getExternalFilesDir(null), "trace_config.textproto")
-
         try {
             textProtoFile.writeBytes(configBytes)
-
             // Start tracing
             if (!helper.startCollecting(textProtoFile.absolutePath, true)) {
                 // TODO: move internal failures to be exceptions
@@ -83,4 +79,4 @@
             throw IllegalStateException("Unable to store perfetto trace")
         }
     }
-}
\ No newline at end of file
+}
diff --git a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.java b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.java
index 6060a4d..fa60360 100644
--- a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.java
+++ b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.java
@@ -19,7 +19,10 @@
 import android.os.SystemClock;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.uiautomator.UiDevice;
 
@@ -31,9 +34,12 @@
 /**
  * PerfettoHelper is used to start and stop the perfetto tracing and move the
  * output perfetto trace file to destination folder.
+ *
+ * @hide
  */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @RequiresApi(28)
-class PerfettoHelper {
+public class PerfettoHelper {
     private static final String LOG_TAG = PerfettoHelper.class.getSimpleName();
     // Command to start the perfetto tracing in the background.
     // perfetto -b -c /data/misc/perfetto-traces/trace_config.pb -o
@@ -51,7 +57,8 @@
     // Check if perfetto is stopped every 5 secs.
     private static final long PERFETTO_KILL_WAIT_TIME = 5000;
 
-    private UiDevice mUIDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+    private final UiDevice mUIDevice =
+            UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
 
     /**
      * Start the perfetto tracing in background using the given config file.
@@ -64,7 +71,7 @@
      * @param isTextProtoConfig true if the config file is textproto format otherwise false.
      * @return true if trace collection started successfully otherwise return false.
      */
-    public boolean startCollecting(String configFilePath, boolean isTextProtoConfig) {
+    public boolean startCollecting(@Nullable String configFilePath, boolean isTextProtoConfig) {
         if (configFilePath == null || configFilePath.isEmpty()) {
             Log.e(LOG_TAG, "Perfetto config file name is null or empty.");
             return false;
@@ -115,7 +122,7 @@
      * @param destinationFile file to copy the perfetto output trace.
      * @return true if the trace collection is successful otherwise false.
      */
-    public boolean stopCollecting(long waitTimeInMsecs, String destinationFile) {
+    public boolean stopCollecting(long waitTimeInMsecs, @NonNull String destinationFile) {
         // Wait for the dump interval before stopping the trace.
         Log.i(LOG_TAG, String.format(
                 "Waiting for %d msecs before stopping perfetto.", waitTimeInMsecs));
@@ -190,13 +197,22 @@
     }
 
     /**
+     * @return the {@link String} path to the temporary output file used to store the trace file
+     * during collection.
+     */
+    @NonNull
+    public static String getPerfettoTmpOutputFilePath() {
+        return PERFETTO_TMP_OUTPUT_FILE;
+    }
+
+    /**
      * Copy the temporary perfetto trace output file from /data/misc/perfetto-traces/ to given
      * destinationFile.
      *
      * @param destinationFile file to copy the perfetto output trace.
      * @return true if the trace file copied successfully otherwise false.
      */
-    private boolean copyFileOutput(String destinationFile) {
+    private boolean copyFileOutput(@NonNull String destinationFile) {
         Path path = Paths.get(destinationFile);
         String destDirectory = path.getParent().toString();
         // Check if the directory already exists
diff --git a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoResultsParser.kt b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoResultsParser.kt
new file mode 100644
index 0000000..dc0d611
--- /dev/null
+++ b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoResultsParser.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.benchmark.perfetto
+
+import org.json.JSONArray
+import org.json.JSONObject
+
+object PerfettoResultsParser {
+    fun parseResult(jsonTrace: String, packageName: String): Map<String, Long> {
+        val map = mutableMapOf<String, Long>()
+        val json = JSONObject(jsonTrace)
+        val androidStartup = json.optJSONObject(ANDROID_STARTUP)
+        if (androidStartup != null) {
+            val startup = androidStartup.optJSONArray(STARTUP)
+            if (startup != null && startup.length() > 0) {
+                parseStartupResult(startup, packageName, map)
+            }
+        }
+        return map
+    }
+
+    private fun parseStartupResult(
+        json: JSONArray,
+        packageName: String,
+        map: MutableMap<String, Long>
+    ) {
+        val length = json.length()
+        for (i in 0 until length) {
+            val startupResult = json.getJSONObject(i)
+            val targetPackageName = startupResult.optString(PACKAGE_NAME)
+            if (packageName == targetPackageName) {
+                val firstFrameMetric = startupResult.optJSONObject(TO_FIRST_FRAME)
+                if (firstFrameMetric != null) {
+                    val duration = firstFrameMetric.optDouble(DUR_MS, 0.0)
+                    map[STARTUP_MS] = duration.toLong()
+                }
+            }
+        }
+    }
+
+    private const val ANDROID_STARTUP = "android_startup"
+    private const val STARTUP = "startup"
+    private const val PACKAGE_NAME = "package_name"
+    private const val TO_FIRST_FRAME = "to_first_frame"
+    private const val DUR_MS = "dur_ms"
+    private const val TIME_ACTIVITY_START = "time_activity_start"
+    private const val TIME_ACTIVITY_RESUME = "time_activity_resume"
+
+    // Metric Keys
+    private const val STARTUP_MS = "perfetto_startupMs"
+}
diff --git a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoTraceParser.kt b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoTraceParser.kt
new file mode 100644
index 0000000..785be51
--- /dev/null
+++ b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoTraceParser.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.benchmark.perfetto
+
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.test.platform.app.InstrumentationRegistry
+import java.io.File
+
+/**
+ * Enables parsing perfetto traces on-device on Q+ devices.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@RequiresApi(21)
+class PerfettoTraceParser {
+
+    /**
+     * The actual [File] path to the `trace_processor_shell`.
+     */
+    var shellFile: File? = null
+
+    /**
+     * Copies `trace_processor_shell` and enables parsing of the perfetto trace files.
+     */
+    fun copyTraceProcessorShell() {
+        val instrumentation = InstrumentationRegistry.getInstrumentation()
+        val context: Context = instrumentation.context
+        shellFile = File(context.cacheDir, "trace_processor_shell")
+        // TODO: support other ABIs
+        if (Build.SUPPORTED_64_BIT_ABIS.isEmpty()) {
+            throw IllegalStateException("Unsupported ABI")
+        }
+        // Write the trace_processor_shell to the external directory so we can process
+        // perfetto metrics on device.
+        val shellFile = shellFile
+        if (shellFile != null && !shellFile.exists()) {
+            val created = shellFile.createNewFile()
+            shellFile.setWritable(true)
+            shellFile.setExecutable(true, false)
+            if (!created) {
+                throw IllegalStateException("Unable to create new file $shellFile")
+            }
+            shellFile.outputStream().use {
+                // TODO: Copy the file based on the ABI
+                context.assets.open("trace_processor_shell_aarch64").copyTo(it)
+            }
+        }
+    }
+}
diff --git a/biometric/biometric-ktx/api/current.txt b/biometric/biometric-ktx/api/current.txt
index d2293f0..1342c5a8 100644
--- a/biometric/biometric-ktx/api/current.txt
+++ b/biometric/biometric-ktx/api/current.txt
@@ -1,22 +1,46 @@
 // Signature format: 4.0
 package androidx.biometric.auth {
 
+  public final class AuthPromptErrorException extends java.lang.Exception {
+    ctor public AuthPromptErrorException(int errorCode, CharSequence errorMessage);
+    method public int getErrorCode();
+    method public CharSequence getErrorMessage();
+    property public final int errorCode;
+    property public final CharSequence errorMessage;
+  }
+
+  public final class AuthPromptFailureException extends java.lang.Exception {
+    ctor public AuthPromptFailureException();
+  }
+
   public final class Class2BiometricAuthExtensionsKt {
+    method public static suspend Object? authenticate(androidx.biometric.auth.Class2BiometricAuthPrompt, androidx.biometric.auth.AuthPromptHost host, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2Biometrics(androidx.fragment.app.FragmentActivity, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2Biometrics(androidx.fragment.app.Fragment, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricAuthentication(androidx.fragment.app.FragmentActivity, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricAuthentication(androidx.fragment.app.Fragment, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
   }
 
   public final class Class2BiometricOrCredentialAuthExtensionsKt {
+    method public static suspend Object? authenticate(androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt, androidx.biometric.auth.AuthPromptHost host, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2BiometricsOrCredentials(androidx.fragment.app.FragmentActivity, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2BiometricsOrCredentials(androidx.fragment.app.Fragment, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricOrCredentialAuthentication(androidx.fragment.app.FragmentActivity, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricOrCredentialAuthentication(androidx.fragment.app.Fragment, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
   }
 
   public final class Class3BiometricAuthExtensionsKt {
-    method public static androidx.biometric.auth.AuthPrompt startClass3BiometricAuthentication(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
-    method public static androidx.biometric.auth.AuthPrompt startClass3BiometricAuthentication(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
+    method public static suspend Object? authenticate(androidx.biometric.auth.Class3BiometricAuthPrompt, androidx.biometric.auth.AuthPromptHost host, androidx.biometric.BiometricPrompt.CryptoObject? crypto, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static androidx.biometric.auth.AuthPrompt authenticateWithClass3Biometrics(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
+    method public static suspend Object? authenticateWithClass3Biometrics(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static androidx.biometric.auth.AuthPrompt authenticateWithClass3Biometrics(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
+    method public static suspend Object? authenticateWithClass3Biometrics(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
   }
 
   public final class Class3BiometricOrCredentialAuthExtensionsKt {
+    method @RequiresApi(android.os.Build.VERSION_CODES.R) public static suspend Object? authenticate(androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt, androidx.biometric.auth.AuthPromptHost host, androidx.biometric.BiometricPrompt.CryptoObject? crypto, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method @RequiresApi(android.os.Build.VERSION_CODES.R) public static suspend Object? authenticateWithClass3BiometricsOrCredentials(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method @RequiresApi(android.os.Build.VERSION_CODES.R) public static suspend Object? authenticateWithClass3BiometricsOrCredentials(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
     method @RequiresApi(android.os.Build.VERSION_CODES.R) public static androidx.biometric.auth.AuthPrompt startClass3BiometricOrCredentialAuthentication(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
     method @RequiresApi(android.os.Build.VERSION_CODES.R) public static androidx.biometric.auth.AuthPrompt startClass3BiometricOrCredentialAuthentication(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
   }
diff --git a/biometric/biometric-ktx/api/public_plus_experimental_current.txt b/biometric/biometric-ktx/api/public_plus_experimental_current.txt
index d2293f0..1342c5a8 100644
--- a/biometric/biometric-ktx/api/public_plus_experimental_current.txt
+++ b/biometric/biometric-ktx/api/public_plus_experimental_current.txt
@@ -1,22 +1,46 @@
 // Signature format: 4.0
 package androidx.biometric.auth {
 
+  public final class AuthPromptErrorException extends java.lang.Exception {
+    ctor public AuthPromptErrorException(int errorCode, CharSequence errorMessage);
+    method public int getErrorCode();
+    method public CharSequence getErrorMessage();
+    property public final int errorCode;
+    property public final CharSequence errorMessage;
+  }
+
+  public final class AuthPromptFailureException extends java.lang.Exception {
+    ctor public AuthPromptFailureException();
+  }
+
   public final class Class2BiometricAuthExtensionsKt {
+    method public static suspend Object? authenticate(androidx.biometric.auth.Class2BiometricAuthPrompt, androidx.biometric.auth.AuthPromptHost host, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2Biometrics(androidx.fragment.app.FragmentActivity, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2Biometrics(androidx.fragment.app.Fragment, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricAuthentication(androidx.fragment.app.FragmentActivity, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricAuthentication(androidx.fragment.app.Fragment, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
   }
 
   public final class Class2BiometricOrCredentialAuthExtensionsKt {
+    method public static suspend Object? authenticate(androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt, androidx.biometric.auth.AuthPromptHost host, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2BiometricsOrCredentials(androidx.fragment.app.FragmentActivity, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2BiometricsOrCredentials(androidx.fragment.app.Fragment, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricOrCredentialAuthentication(androidx.fragment.app.FragmentActivity, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricOrCredentialAuthentication(androidx.fragment.app.Fragment, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
   }
 
   public final class Class3BiometricAuthExtensionsKt {
-    method public static androidx.biometric.auth.AuthPrompt startClass3BiometricAuthentication(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
-    method public static androidx.biometric.auth.AuthPrompt startClass3BiometricAuthentication(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
+    method public static suspend Object? authenticate(androidx.biometric.auth.Class3BiometricAuthPrompt, androidx.biometric.auth.AuthPromptHost host, androidx.biometric.BiometricPrompt.CryptoObject? crypto, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static androidx.biometric.auth.AuthPrompt authenticateWithClass3Biometrics(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
+    method public static suspend Object? authenticateWithClass3Biometrics(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static androidx.biometric.auth.AuthPrompt authenticateWithClass3Biometrics(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
+    method public static suspend Object? authenticateWithClass3Biometrics(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
   }
 
   public final class Class3BiometricOrCredentialAuthExtensionsKt {
+    method @RequiresApi(android.os.Build.VERSION_CODES.R) public static suspend Object? authenticate(androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt, androidx.biometric.auth.AuthPromptHost host, androidx.biometric.BiometricPrompt.CryptoObject? crypto, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method @RequiresApi(android.os.Build.VERSION_CODES.R) public static suspend Object? authenticateWithClass3BiometricsOrCredentials(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method @RequiresApi(android.os.Build.VERSION_CODES.R) public static suspend Object? authenticateWithClass3BiometricsOrCredentials(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
     method @RequiresApi(android.os.Build.VERSION_CODES.R) public static androidx.biometric.auth.AuthPrompt startClass3BiometricOrCredentialAuthentication(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
     method @RequiresApi(android.os.Build.VERSION_CODES.R) public static androidx.biometric.auth.AuthPrompt startClass3BiometricOrCredentialAuthentication(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
   }
diff --git a/biometric/biometric-ktx/api/restricted_current.txt b/biometric/biometric-ktx/api/restricted_current.txt
index d2293f0..1342c5a8 100644
--- a/biometric/biometric-ktx/api/restricted_current.txt
+++ b/biometric/biometric-ktx/api/restricted_current.txt
@@ -1,22 +1,46 @@
 // Signature format: 4.0
 package androidx.biometric.auth {
 
+  public final class AuthPromptErrorException extends java.lang.Exception {
+    ctor public AuthPromptErrorException(int errorCode, CharSequence errorMessage);
+    method public int getErrorCode();
+    method public CharSequence getErrorMessage();
+    property public final int errorCode;
+    property public final CharSequence errorMessage;
+  }
+
+  public final class AuthPromptFailureException extends java.lang.Exception {
+    ctor public AuthPromptFailureException();
+  }
+
   public final class Class2BiometricAuthExtensionsKt {
+    method public static suspend Object? authenticate(androidx.biometric.auth.Class2BiometricAuthPrompt, androidx.biometric.auth.AuthPromptHost host, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2Biometrics(androidx.fragment.app.FragmentActivity, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2Biometrics(androidx.fragment.app.Fragment, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricAuthentication(androidx.fragment.app.FragmentActivity, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricAuthentication(androidx.fragment.app.Fragment, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
   }
 
   public final class Class2BiometricOrCredentialAuthExtensionsKt {
+    method public static suspend Object? authenticate(androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt, androidx.biometric.auth.AuthPromptHost host, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2BiometricsOrCredentials(androidx.fragment.app.FragmentActivity, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static suspend Object? authenticateWithClass2BiometricsOrCredentials(androidx.fragment.app.Fragment, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricOrCredentialAuthentication(androidx.fragment.app.FragmentActivity, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
     method public static androidx.biometric.auth.AuthPrompt startClass2BiometricOrCredentialAuthentication(androidx.fragment.app.Fragment, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
   }
 
   public final class Class3BiometricAuthExtensionsKt {
-    method public static androidx.biometric.auth.AuthPrompt startClass3BiometricAuthentication(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
-    method public static androidx.biometric.auth.AuthPrompt startClass3BiometricAuthentication(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
+    method public static suspend Object? authenticate(androidx.biometric.auth.Class3BiometricAuthPrompt, androidx.biometric.auth.AuthPromptHost host, androidx.biometric.BiometricPrompt.CryptoObject? crypto, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static androidx.biometric.auth.AuthPrompt authenticateWithClass3Biometrics(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
+    method public static suspend Object? authenticateWithClass3Biometrics(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method public static androidx.biometric.auth.AuthPrompt authenticateWithClass3Biometrics(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
+    method public static suspend Object? authenticateWithClass3Biometrics(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, CharSequence negativeButtonText, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
   }
 
   public final class Class3BiometricOrCredentialAuthExtensionsKt {
+    method @RequiresApi(android.os.Build.VERSION_CODES.R) public static suspend Object? authenticate(androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt, androidx.biometric.auth.AuthPromptHost host, androidx.biometric.BiometricPrompt.CryptoObject? crypto, kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method @RequiresApi(android.os.Build.VERSION_CODES.R) public static suspend Object? authenticateWithClass3BiometricsOrCredentials(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
+    method @RequiresApi(android.os.Build.VERSION_CODES.R) public static suspend Object? authenticateWithClass3BiometricsOrCredentials(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional kotlin.coroutines.Continuation<? super androidx.biometric.BiometricPrompt.AuthenticationResult> p);
     method @RequiresApi(android.os.Build.VERSION_CODES.R) public static androidx.biometric.auth.AuthPrompt startClass3BiometricOrCredentialAuthentication(androidx.fragment.app.FragmentActivity, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
     method @RequiresApi(android.os.Build.VERSION_CODES.R) public static androidx.biometric.auth.AuthPrompt startClass3BiometricOrCredentialAuthentication(androidx.fragment.app.Fragment, androidx.biometric.BiometricPrompt.CryptoObject? crypto, CharSequence title, optional CharSequence? subtitle, optional CharSequence? description, optional boolean confirmationRequired, optional java.util.concurrent.Executor? executor, androidx.biometric.auth.AuthPromptCallback callback);
   }
diff --git a/biometric/biometric-ktx/build.gradle b/biometric/biometric-ktx/build.gradle
index 570ffa1..4dc85dc 100755
--- a/biometric/biometric-ktx/build.gradle
+++ b/biometric/biometric-ktx/build.gradle
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-import static androidx.build.dependencies.DependenciesKt.*
+
 import androidx.build.LibraryGroups
 import androidx.build.Publish
 
+import static androidx.build.dependencies.DependenciesKt.getKOTLIN_COROUTINES_CORE
+import static androidx.build.dependencies.DependenciesKt.getKOTLIN_STDLIB
+
 
 plugins {
     id("AndroidXPlugin")
@@ -27,6 +30,7 @@
 
 dependencies {
     api(KOTLIN_STDLIB)
+    api(KOTLIN_COROUTINES_CORE)
     api(project(":biometric:biometric"))
 }
 
@@ -36,4 +40,4 @@
     mavenGroup = LibraryGroups.BIOMETRIC
     inceptionYear = "2020"
     description = "Kotlin extensions for the Biometric Library."
-}
\ No newline at end of file
+}
diff --git a/biometric/biometric-ktx/samples/build.gradle b/biometric/biometric-ktx/samples/build.gradle
new file mode 100644
index 0000000..bf95a76
--- /dev/null
+++ b/biometric/biometric-ktx/samples/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * 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 androidx.build.LibraryGroups
+import androidx.build.LibraryType
+import androidx.build.LibraryVersions
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+}
+
+dependencies {
+    compileOnly(project(":annotation:annotation-sampled"))
+    implementation(project(":biometric:biometric-ktx"))
+}
+
+androidx {
+    name = "AndroidX Biometric Samples"
+    type = LibraryType.SAMPLES
+    mavenGroup = LibraryGroups.BIOMETRIC
+    inceptionYear = "2021"
+    description = "Contains the sample code for the AndroidX Biometric library"
+}
diff --git a/biometric/biometric-ktx/samples/lint-baseline.xml b/biometric/biometric-ktx/samples/lint-baseline.xml
new file mode 100644
index 0000000..8f1aa4b
--- /dev/null
+++ b/biometric/biometric-ktx/samples/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.2.0-beta02" client="gradle" variant="debug" version="4.2.0-beta02">
+
+</issues>
diff --git a/biometric/biometric-ktx/samples/src/main/AndroidManifest.xml b/biometric/biometric-ktx/samples/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1dacecc
--- /dev/null
+++ b/biometric/biometric-ktx/samples/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 xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.biometric.samples"/>
diff --git a/biometric/biometric-ktx/samples/src/main/java/androidx/biometric/samples/auth/CoroutineSamples.kt b/biometric/biometric-ktx/samples/src/main/java/androidx/biometric/samples/auth/CoroutineSamples.kt
new file mode 100644
index 0000000..4b7aeed
--- /dev/null
+++ b/biometric/biometric-ktx/samples/src/main/java/androidx/biometric/samples/auth/CoroutineSamples.kt
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.biometric.samples.auth
+
+import android.security.keystore.KeyGenParameterSpec
+import android.security.keystore.KeyProperties
+import androidx.annotation.Sampled
+import androidx.biometric.BiometricPrompt
+import androidx.biometric.auth.AuthPromptErrorException
+import androidx.biometric.auth.AuthPromptFailureException
+import androidx.biometric.auth.AuthPromptHost
+import androidx.biometric.auth.Class2BiometricAuthPrompt
+import androidx.biometric.auth.Class2BiometricOrCredentialAuthPrompt
+import androidx.biometric.auth.Class3BiometricAuthPrompt
+import androidx.biometric.auth.Class3BiometricOrCredentialAuthPrompt
+import androidx.biometric.auth.authenticate
+import androidx.fragment.app.Fragment
+import java.nio.charset.Charset
+import java.security.KeyStore
+import javax.crypto.Cipher
+import javax.crypto.KeyGenerator
+import javax.crypto.SecretKey
+
+// Stubbed definitions for samples
+private const val KEYSTORE_INSTANCE = "AndroidKeyStore"
+private const val KEY_NAME = "mySecretKey"
+private const val title = ""
+private const val subtitle = ""
+private const val description = ""
+private const val negativeButtonText = ""
+private fun sendEncryptedPayload(payload: ByteArray?): ByteArray? = payload
+
+@Sampled
+suspend fun Fragment.class2BiometricAuth() {
+    val payload = "A message to encrypt".toByteArray(Charset.defaultCharset())
+
+    // Construct AuthPrompt with localized Strings to be displayed to UI.
+    val authPrompt = Class2BiometricAuthPrompt.Builder(title, negativeButtonText).apply {
+        setSubtitle(subtitle)
+        setDescription(description)
+        setConfirmationRequired(true)
+    }.build()
+
+    try {
+        val authResult = authPrompt.authenticate(AuthPromptHost(this))
+
+        // Encrypt a payload using the result of crypto-based auth.
+        val encryptedPayload = authResult.cryptoObject?.cipher?.doFinal(payload)
+
+        // Use the encrypted payload somewhere interesting.
+        sendEncryptedPayload(encryptedPayload)
+    } catch (e: AuthPromptErrorException) {
+        // Handle irrecoverable error during authentication.
+        // Possible values for AuthPromptErrorException.errorCode are listed in the @IntDef,
+        // androidx.biometric.BiometricPrompt.AuthenticationError.
+    } catch (e: AuthPromptFailureException) {
+        // Handle auth failure due biometric credentials being rejected.
+    }
+}
+
+@Sampled
+suspend fun Fragment.class2BiometricOrCredentialAuth() {
+    val payload = "A message to encrypt".toByteArray(Charset.defaultCharset())
+
+    // Construct AuthPrompt with localized Strings to be displayed to UI.
+    val authPrompt = Class2BiometricOrCredentialAuthPrompt.Builder(title).apply {
+        setSubtitle(subtitle)
+        setDescription(description)
+        setConfirmationRequired(true)
+    }.build()
+
+    try {
+        val authResult = authPrompt.authenticate(AuthPromptHost(this))
+
+        // Encrypt a payload using the result of crypto-based auth.
+        val encryptedPayload = authResult.cryptoObject?.cipher?.doFinal(payload)
+
+        // Use the encrypted payload somewhere interesting.
+        sendEncryptedPayload(encryptedPayload)
+    } catch (e: AuthPromptErrorException) {
+        // Handle irrecoverable error during authentication.
+        // Possible values for AuthPromptErrorException.errorCode are listed in the @IntDef,
+        // androidx.biometric.BiometricPrompt.AuthenticationError.
+    } catch (e: AuthPromptFailureException) {
+        // Handle auth failure due biometric credentials being rejected.
+    }
+}
+
+@Sampled
+@Suppress("UnsafeNewApiCall", "NewApi")
+suspend fun Fragment.class3BiometricAuth() {
+    // To use Class3 authentication, we need to create a CryptoObject.
+    // First create a spec for the key to be generated.
+    val keyPurpose = KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
+    val keySpec = KeyGenParameterSpec.Builder(KEY_NAME, keyPurpose).apply {
+        setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+        setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+        setUserAuthenticationRequired(true)
+
+        // Require authentication for each use of the key.
+        val timeout = 0
+        // Set the key type according to the allowed auth types.
+        val keyType =
+            KeyProperties.AUTH_BIOMETRIC_STRONG or KeyProperties.AUTH_DEVICE_CREDENTIAL
+        setUserAuthenticationParameters(timeout, keyType)
+    }.build()
+
+    // Generate and store the key in the Android keystore.
+    KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_INSTANCE).run {
+        init(keySpec)
+        generateKey()
+    }
+
+    // Prepare the crypto object to use for authentication.
+    val cipher = Cipher.getInstance(
+        "${KeyProperties.KEY_ALGORITHM_AES}/${KeyProperties.BLOCK_MODE_CBC}/" +
+            KeyProperties.ENCRYPTION_PADDING_PKCS7
+    ).apply {
+        val keyStore = KeyStore.getInstance(KEYSTORE_INSTANCE).apply { load(null) }
+        init(Cipher.ENCRYPT_MODE, keyStore.getKey(KEY_NAME, null) as SecretKey)
+    }
+
+    val cryptoObject = BiometricPrompt.CryptoObject(cipher)
+    val payload = "A message to encrypt".toByteArray(Charset.defaultCharset())
+
+    // Construct AuthPrompt with localized Strings to be displayed to UI.
+    val authPrompt = Class3BiometricAuthPrompt.Builder(title, negativeButtonText).apply {
+        setSubtitle(subtitle)
+        setDescription(description)
+        setConfirmationRequired(true)
+    }.build()
+
+    try {
+        val authResult = authPrompt.authenticate(AuthPromptHost(this), cryptoObject)
+
+        // Encrypt a payload using the result of crypto-based auth.
+        val encryptedPayload = authResult.cryptoObject?.cipher?.doFinal(payload)
+
+        // Use the encrypted payload somewhere interesting.
+        sendEncryptedPayload(encryptedPayload)
+    } catch (e: AuthPromptErrorException) {
+        // Handle irrecoverable error during authentication.
+        // Possible values for AuthPromptErrorException.errorCode are listed in the @IntDef,
+        // androidx.biometric.BiometricPrompt.AuthenticationError.
+    } catch (e: AuthPromptFailureException) {
+        // Handle auth failure due biometric credentials being rejected.
+    }
+}
+
+@Sampled
+@Suppress("UnsafeNewApiCall", "NewApi")
+suspend fun Fragment.class3BiometricOrCredentialAuth() {
+    // To use Class3 authentication, we need to create a CryptoObject.
+    // First create a spec for the key to be generated.
+    val keyPurpose = KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
+    val keySpec = KeyGenParameterSpec.Builder(KEY_NAME, keyPurpose).apply {
+        setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+        setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+        setUserAuthenticationRequired(true)
+
+        // Require authentication for each use of the key.
+        val timeout = 0
+        // Set the key type according to the allowed auth types.
+        val keyType =
+            KeyProperties.AUTH_BIOMETRIC_STRONG or KeyProperties.AUTH_DEVICE_CREDENTIAL
+        setUserAuthenticationParameters(timeout, keyType)
+    }.build()
+
+    // Generate and store the key in the Android keystore.
+    KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_INSTANCE).run {
+        init(keySpec)
+        generateKey()
+    }
+
+    // Prepare the crypto object to use for authentication.
+    val cipher = Cipher.getInstance(
+        "${KeyProperties.KEY_ALGORITHM_AES}/${KeyProperties.BLOCK_MODE_CBC}/" +
+            KeyProperties.ENCRYPTION_PADDING_PKCS7
+    ).apply {
+        val keyStore = KeyStore.getInstance(KEYSTORE_INSTANCE).apply { load(null) }
+        init(Cipher.ENCRYPT_MODE, keyStore.getKey(KEY_NAME, null) as SecretKey)
+    }
+
+    val cryptoObject = BiometricPrompt.CryptoObject(cipher)
+    val payload = "A message to encrypt".toByteArray(Charset.defaultCharset())
+
+    // Construct AuthPrompt with localized Strings to be displayed to UI.
+    val authPrompt = Class3BiometricOrCredentialAuthPrompt.Builder(title).apply {
+        setSubtitle(subtitle)
+        setDescription(description)
+        setConfirmationRequired(true)
+    }.build()
+
+    try {
+        val authResult = authPrompt.authenticate(AuthPromptHost(this), cryptoObject)
+
+        // Encrypt a payload using the result of crypto-based auth.
+        val encryptedPayload = authResult.cryptoObject?.cipher?.doFinal(payload)
+
+        // Use the encrypted payload somewhere interesting.
+        sendEncryptedPayload(encryptedPayload)
+    } catch (e: AuthPromptErrorException) {
+        // Handle irrecoverable error during authentication.
+        // Possible values for AuthPromptErrorException.errorCode are listed in the @IntDef,
+        // androidx.biometric.BiometricPrompt.AuthenticationError.
+    } catch (e: AuthPromptFailureException) {
+        // Handle auth failure due biometric credentials being rejected.
+    }
+}
diff --git a/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/AuthPromptErrorException.kt b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/AuthPromptErrorException.kt
new file mode 100644
index 0000000..c1daef7
--- /dev/null
+++ b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/AuthPromptErrorException.kt
@@ -0,0 +1,27 @@
+/*
+ * 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
+/**
+ * Thrown when an unrecoverable error has been encountered and authentication has stopped.
+ *
+ * @param errorCode An integer ID associated with the error.
+ * @param errorMessage A human-readable string that describes the error.
+ */
+public class AuthPromptErrorException(
+    public val errorCode: Int,
+    public val errorMessage: CharSequence
+) : Exception(errorMessage.toString())
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.java b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/AuthPromptFailureException.kt
similarity index 68%
copy from navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.java
copy to biometric/biometric-ktx/src/main/java/androidx/biometric/auth/AuthPromptFailureException.kt
index f9f0fbb..6a1c34e 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.java
+++ b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/AuthPromptFailureException.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,10 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.navigation;
+package androidx.biometric.auth
 
 /**
- * An interface marking generated Args classes.
+ * Thrown when an authentication attempt by the user has been rejected, e.g., the user's
+ * biometrics were not recognized.
  */
-public interface NavArgs {
-}
+public class AuthPromptFailureException : Exception()
diff --git a/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class2BiometricAuthExtensions.kt b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class2BiometricAuthExtensions.kt
index 4d67b8c..4c43052 100755
--- a/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class2BiometricAuthExtensions.kt
+++ b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class2BiometricAuthExtensions.kt
@@ -15,11 +15,44 @@
  */
 package androidx.biometric.auth
 
+import androidx.biometric.BiometricPrompt.AuthenticationResult
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentActivity
+import kotlinx.coroutines.suspendCancellableCoroutine
 import java.util.concurrent.Executor
 
 /**
+ * Shows an authentication prompt to the user.
+ *
+ * @param host A wrapper for the component that will host the prompt.
+ *
+ * @return [AuthenticationResult] for a successful authentication.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class2BiometricAuthPrompt.authenticate(AuthPromptHost, AuthPromptCallback)
+ *
+ * @sample androidx.biometric.samples.auth.class2BiometricAuth
+ */
+public suspend fun Class2BiometricAuthPrompt.authenticate(
+    host: AuthPromptHost,
+): AuthenticationResult {
+    return suspendCancellableCoroutine { continuation ->
+        val authPrompt = startAuthentication(
+            host,
+            Runnable::run,
+            CoroutineAuthPromptCallback(continuation)
+        )
+
+        continuation.invokeOnCancellation {
+            authPrompt.cancelAuthentication()
+        }
+    }
+}
+
+/**
  * Prompts the user to authenticate with a **Class 2** biometric (e.g. fingerprint, face, or iris).
  *
  * Note that **Class 3** biometrics are guaranteed to meet the requirements for **Class 2** and thus
@@ -68,6 +101,44 @@
  * @param subtitle An optional subtitle to be displayed on the prompt.
  * @param description An optional description to be displayed on the prompt.
  * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
+ *
+ * @return [AuthenticationResult] for a successful authentication.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class2BiometricAuthPrompt
+ */
+public suspend fun FragmentActivity.authenticateWithClass2Biometrics(
+    title: CharSequence,
+    negativeButtonText: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): AuthenticationResult {
+    val authPrompt = buildClass2BiometricAuthPrompt(
+        title,
+        negativeButtonText,
+        subtitle,
+        description,
+        confirmationRequired,
+    )
+
+    return authPrompt.authenticate(AuthPromptHost(this))
+}
+
+/**
+ * Prompts the user to authenticate with a **Class 2** biometric (e.g. fingerprint, face, or iris).
+ *
+ * Note that **Class 3** biometrics are guaranteed to meet the requirements for **Class 2** and thus
+ * will also be accepted.
+ *
+ * @param title The title to be displayed on the prompt.
+ * @param negativeButtonText The label for the negative button on the prompt.
+ * @param subtitle An optional subtitle to be displayed on the prompt.
+ * @param description An optional description to be displayed on the prompt.
+ * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
  * @param executor An executor for [callback] methods. If `null`, these will run on the main thread.
  * @param callback The object that will receive and process authentication events.
  * @return An [AuthPrompt] handle to the shown prompt.
@@ -96,6 +167,61 @@
 }
 
 /**
+ * Prompts the user to authenticate with a **Class 2** biometric (e.g. fingerprint, face, or iris).
+ *
+ * Note that **Class 3** biometrics are guaranteed to meet the requirements for **Class 2** and thus
+ * will also be accepted.
+ *
+ * @param title The title to be displayed on the prompt.
+ * @param negativeButtonText The label for the negative button on the prompt.
+ * @param subtitle An optional subtitle to be displayed on the prompt.
+ * @param description An optional description to be displayed on the prompt.
+ * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
+ *
+ * @return [AuthenticationResult] for a successful authentication.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class2BiometricAuthPrompt
+ */
+public suspend fun Fragment.authenticateWithClass2Biometrics(
+    title: CharSequence,
+    negativeButtonText: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): AuthenticationResult {
+    val authPrompt = buildClass2BiometricAuthPrompt(
+        title,
+        negativeButtonText,
+        subtitle,
+        description,
+        confirmationRequired,
+    )
+
+    return authPrompt.authenticate(AuthPromptHost(this))
+}
+
+/**
+ * Creates a [Class2BiometricAuthPrompt] with the given parameters.
+ */
+private fun buildClass2BiometricAuthPrompt(
+    title: CharSequence,
+    negativeButtonText: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): Class2BiometricAuthPrompt = Class2BiometricAuthPrompt.Builder(title, negativeButtonText)
+    .apply {
+        subtitle?.let { setSubtitle(it) }
+        description?.let { setDescription(it) }
+        setConfirmationRequired(confirmationRequired)
+    }
+    .build()
+
+/**
  * Creates a [Class2BiometricAuthPrompt] with the given parameters and starts authentication.
  */
 private fun startClass2BiometricAuthenticationInternal(
@@ -108,15 +234,17 @@
     executor: Executor? = null,
     callback: AuthPromptCallback
 ): AuthPrompt {
-    val prompt = Class2BiometricAuthPrompt.Builder(title, negativeButtonText).apply {
-        subtitle?.let { setSubtitle(it) }
-        description?.let { setDescription(it) }
-        setConfirmationRequired(confirmationRequired)
-    }.build()
+    val prompt = buildClass2BiometricAuthPrompt(
+        title,
+        negativeButtonText,
+        subtitle,
+        description,
+        confirmationRequired
+    )
 
     return if (executor == null) {
         prompt.startAuthentication(host, callback)
     } else {
         prompt.startAuthentication(host, executor, callback)
     }
-}
\ No newline at end of file
+}
diff --git a/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class2BiometricOrCredentialAuthExtensions.kt b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class2BiometricOrCredentialAuthExtensions.kt
index 0b3802f..8c772e3 100755
--- a/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class2BiometricOrCredentialAuthExtensions.kt
+++ b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class2BiometricOrCredentialAuthExtensions.kt
@@ -15,11 +15,47 @@
  */
 package androidx.biometric.auth
 
+import androidx.biometric.BiometricPrompt.AuthenticationResult
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentActivity
+import kotlinx.coroutines.suspendCancellableCoroutine
 import java.util.concurrent.Executor
 
 /**
+ * Shows an authentication prompt to the user.
+ *
+ * @param host A wrapper for the component that will host the prompt.
+ *
+ * @return [AuthenticationResult] for a successful authentication.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class2BiometricOrCredentialAuthPrompt.authenticate(
+ *     AuthPromptHost,
+ *     AuthPromptCallback
+ * )
+ *
+ * @sample androidx.biometric.samples.auth.class2BiometricOrCredentialAuth
+ */
+public suspend fun Class2BiometricOrCredentialAuthPrompt.authenticate(
+    host: AuthPromptHost,
+): AuthenticationResult {
+    return suspendCancellableCoroutine { continuation ->
+        val authPrompt = startAuthentication(
+            host,
+            Runnable::run,
+            CoroutineAuthPromptCallback(continuation)
+        )
+
+        continuation.invokeOnCancellation {
+            authPrompt.cancelAuthentication()
+        }
+    }
+}
+
+/**
  * Prompts the user to authenticate with a **Class 2** biometric (e.g. fingerprint, face, or iris)
  * or the screen lock credential (i.e. PIN, pattern, or password) for the device.
  *
@@ -66,6 +102,42 @@
  * @param subtitle An optional subtitle to be displayed on the prompt.
  * @param description An optional description to be displayed on the prompt.
  * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
+ *
+ * @return [AuthenticationResult] for a successful authentication.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class2BiometricOrCredentialAuthPrompt
+ */
+public suspend fun FragmentActivity.authenticateWithClass2BiometricsOrCredentials(
+    title: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): AuthenticationResult {
+    val authPrompt = buildClass2BiometricOrCredentialAuthPrompt(
+        title,
+        subtitle,
+        description,
+        confirmationRequired,
+    )
+
+    return authPrompt.authenticate(AuthPromptHost(this))
+}
+
+/**
+ * Prompts the user to authenticate with a **Class 2** biometric (e.g. fingerprint, face, or iris)
+ * or the screen lock credential (i.e. PIN, pattern, or password) for the device.
+ *
+ * Note that **Class 3** biometrics are guaranteed to meet the requirements for **Class 2** and thus
+ * will also be accepted.
+ *
+ * @param title The title to be displayed on the prompt.
+ * @param subtitle An optional subtitle to be displayed on the prompt.
+ * @param description An optional description to be displayed on the prompt.
+ * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
  * @param executor An executor for [callback] methods. If `null`, these will run on the main thread.
  * @param callback The object that will receive and process authentication events.
  * @return An [AuthPrompt] handle to the shown prompt.
@@ -92,6 +164,57 @@
 }
 
 /**
+ * Prompts the user to authenticate with a **Class 2** biometric (e.g. fingerprint, face, or iris)
+ * or the screen lock credential (i.e. PIN, pattern, or password) for the device.
+ *
+ * Note that **Class 3** biometrics are guaranteed to meet the requirements for **Class 2** and thus
+ * will also be accepted.
+ *
+ * @param title The title to be displayed on the prompt.
+ * @param subtitle An optional subtitle to be displayed on the prompt.
+ * @param description An optional description to be displayed on the prompt.
+ * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
+ * @return An [AuthPrompt] handle to the shown prompt.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class2BiometricOrCredentialAuthPrompt
+ */
+public suspend fun Fragment.authenticateWithClass2BiometricsOrCredentials(
+    title: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): AuthenticationResult {
+    val authPrompt = buildClass2BiometricOrCredentialAuthPrompt(
+        title,
+        subtitle,
+        description,
+        confirmationRequired,
+    )
+
+    return authPrompt.authenticate(AuthPromptHost(this))
+}
+
+/**
+ * Creates a [Class2BiometricOrCredentialAuthPrompt] with the given parameters.
+ */
+private fun buildClass2BiometricOrCredentialAuthPrompt(
+    title: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): Class2BiometricOrCredentialAuthPrompt = Class2BiometricOrCredentialAuthPrompt.Builder(title)
+    .apply {
+        subtitle?.let { setSubtitle(it) }
+        description?.let { setDescription(it) }
+        setConfirmationRequired(confirmationRequired)
+    }
+    .build()
+
+/**
  * Creates a [Class2BiometricOrCredentialAuthPrompt] with the given parameters and starts
  * authentication.
  */
@@ -104,15 +227,16 @@
     executor: Executor? = null,
     callback: AuthPromptCallback
 ): AuthPrompt {
-    val prompt = Class2BiometricOrCredentialAuthPrompt.Builder(title).apply {
-        subtitle?.let { setSubtitle(it) }
-        description?.let { setDescription(it) }
-        setConfirmationRequired(confirmationRequired)
-    }.build()
+    val prompt = buildClass2BiometricOrCredentialAuthPrompt(
+        title,
+        subtitle,
+        description,
+        confirmationRequired,
+    )
 
     return if (executor == null) {
         prompt.startAuthentication(host, callback)
     } else {
         prompt.startAuthentication(host, executor, callback)
     }
-}
\ No newline at end of file
+}
diff --git a/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class3BiometricAuthExtensions.kt b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class3BiometricAuthExtensions.kt
index cd7a639..0e24d45 100755
--- a/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class3BiometricAuthExtensions.kt
+++ b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class3BiometricAuthExtensions.kt
@@ -15,12 +15,48 @@
  */
 package androidx.biometric.auth
 
-import androidx.biometric.BiometricPrompt
+import androidx.biometric.BiometricPrompt.AuthenticationResult
+import androidx.biometric.BiometricPrompt.CryptoObject
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentActivity
+import kotlinx.coroutines.suspendCancellableCoroutine
 import java.util.concurrent.Executor
 
 /**
+ * Shows an authentication prompt to the user.
+ *
+ * @param host A wrapper for the component that will host the prompt.
+ * @param crypto A cryptographic object to be associated with this authentication.
+ *
+ * @return [AuthenticationResult] for a successful authentication.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class3BiometricAuthPrompt.authenticate(AuthPromptHost, AuthPromptCallback)
+ *
+ * @sample androidx.biometric.samples.auth.class3BiometricAuth
+ */
+public suspend fun Class3BiometricAuthPrompt.authenticate(
+    host: AuthPromptHost,
+    crypto: CryptoObject?,
+): AuthenticationResult {
+    return suspendCancellableCoroutine { continuation ->
+        val authPrompt = startAuthentication(
+            host,
+            crypto,
+            Runnable::run,
+            CoroutineAuthPromptCallback(continuation)
+        )
+
+        continuation.invokeOnCancellation {
+            authPrompt.cancelAuthentication()
+        }
+    }
+}
+
+/**
  * Prompts the user to authenticate with a **Class 3** biometric (e.g. fingerprint, face, or iris).
  *
  * @param crypto A cryptographic object to be associated with this authentication.
@@ -35,8 +71,8 @@
  *
  * @see Class3BiometricAuthPrompt
  */
-public fun FragmentActivity.startClass3BiometricAuthentication(
-    crypto: BiometricPrompt.CryptoObject?,
+public fun FragmentActivity.authenticateWithClass3Biometrics(
+    crypto: CryptoObject?,
     title: CharSequence,
     negativeButtonText: CharSequence,
     subtitle: CharSequence? = null,
@@ -67,14 +103,51 @@
  * @param subtitle An optional subtitle to be displayed on the prompt.
  * @param description An optional description to be displayed on the prompt.
  * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
+ *
+ * @return [AuthenticationResult] for a successful authentication.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class3BiometricAuthPrompt
+ */
+public suspend fun FragmentActivity.authenticateWithClass3Biometrics(
+    crypto: CryptoObject?,
+    title: CharSequence,
+    negativeButtonText: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): AuthenticationResult {
+    val authPrompt = buildClass3BiometricAuthPrompt(
+        title,
+        negativeButtonText,
+        subtitle,
+        description,
+        confirmationRequired
+    )
+
+    return authPrompt.authenticate(AuthPromptHost(this), crypto)
+}
+
+/**
+ * Prompts the user to authenticate with a **Class 3** biometric (e.g. fingerprint, face, or iris).
+ *
+ * @param crypto A cryptographic object to be associated with this authentication.
+ * @param title The title to be displayed on the prompt.
+ * @param negativeButtonText The label for the negative button on the prompt.
+ * @param subtitle An optional subtitle to be displayed on the prompt.
+ * @param description An optional description to be displayed on the prompt.
+ * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
  * @param executor An executor for [callback] methods. If `null`, these will run on the main thread.
  * @param callback The object that will receive and process authentication events.
  * @return An [AuthPrompt] handle to the shown prompt.
  *
  * @see Class3BiometricAuthPrompt
  */
-public fun Fragment.startClass3BiometricAuthentication(
-    crypto: BiometricPrompt.CryptoObject?,
+public fun Fragment.authenticateWithClass3Biometrics(
+    crypto: CryptoObject?,
     title: CharSequence,
     negativeButtonText: CharSequence,
     subtitle: CharSequence? = null,
@@ -97,11 +170,65 @@
 }
 
 /**
+ * Prompts the user to authenticate with a **Class 3** biometric (e.g. fingerprint, face, or iris).
+ *
+ * @param crypto A cryptographic object to be associated with this authentication.
+ * @param title The title to be displayed on the prompt.
+ * @param negativeButtonText The label for the negative button on the prompt.
+ * @param subtitle An optional subtitle to be displayed on the prompt.
+ * @param description An optional description to be displayed on the prompt.
+ * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
+ *
+ * @return [AuthenticationResult] for a successful authentication.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class3BiometricAuthPrompt
+ */
+public suspend fun Fragment.authenticateWithClass3Biometrics(
+    crypto: CryptoObject?,
+    title: CharSequence,
+    negativeButtonText: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): AuthenticationResult {
+    val authPrompt = buildClass3BiometricAuthPrompt(
+        title,
+        negativeButtonText,
+        subtitle,
+        description,
+        confirmationRequired
+    )
+
+    return authPrompt.authenticate(AuthPromptHost(this), crypto)
+}
+
+/**
+ * Creates a [Class3BiometricAuthPrompt] with the given parameters.
+ */
+private fun buildClass3BiometricAuthPrompt(
+    title: CharSequence,
+    negativeButtonText: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): Class3BiometricAuthPrompt = Class3BiometricAuthPrompt.Builder(title, negativeButtonText)
+    .apply {
+        subtitle?.let { setSubtitle(it) }
+        description?.let { setDescription(it) }
+        setConfirmationRequired(confirmationRequired)
+    }
+    .build()
+
+/**
  * Creates a [Class3BiometricAuthPrompt] with the given parameters and starts authentication.
  */
 private fun startClass3BiometricAuthenticationInternal(
     host: AuthPromptHost,
-    crypto: BiometricPrompt.CryptoObject?,
+    crypto: CryptoObject?,
     title: CharSequence,
     negativeButtonText: CharSequence,
     subtitle: CharSequence? = null,
@@ -110,15 +237,17 @@
     executor: Executor? = null,
     callback: AuthPromptCallback
 ): AuthPrompt {
-    val prompt = Class3BiometricAuthPrompt.Builder(title, negativeButtonText).apply {
-        subtitle?.let { setSubtitle(it) }
-        description?.let { setDescription(it) }
-        setConfirmationRequired(confirmationRequired)
-    }.build()
+    val prompt = buildClass3BiometricAuthPrompt(
+        title,
+        negativeButtonText,
+        subtitle,
+        description,
+        confirmationRequired
+    )
 
     return if (executor == null) {
         prompt.startAuthentication(host, crypto, callback)
     } else {
         prompt.startAuthentication(host, crypto, executor, callback)
     }
-}
\ No newline at end of file
+}
diff --git a/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class3BiometricOrCredentialAuthExtensions.kt b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class3BiometricOrCredentialAuthExtensions.kt
index 1376e36..ec27ec6 100755
--- a/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class3BiometricOrCredentialAuthExtensions.kt
+++ b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/Class3BiometricOrCredentialAuthExtensions.kt
@@ -17,12 +17,52 @@
 
 import android.os.Build
 import androidx.annotation.RequiresApi
-import androidx.biometric.BiometricPrompt
+import androidx.biometric.BiometricPrompt.AuthenticationResult
+import androidx.biometric.BiometricPrompt.CryptoObject
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentActivity
+import kotlinx.coroutines.suspendCancellableCoroutine
 import java.util.concurrent.Executor
 
 /**
+ * Shows an authentication prompt to the user.
+ *
+ * @param host A wrapper for the component that will host the prompt.
+ * @param crypto A cryptographic object to be associated with this authentication.
+ *
+ * @return [AuthenticationResult] for a successful authentication.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class3BiometricOrCredentialAuthPrompt.authenticate(
+ *     AuthPromptHost,
+ *     AuthPromptCallback
+ * )
+ *
+ * @sample androidx.biometric.samples.auth.class3BiometricOrCredentialAuth
+ */
+@RequiresApi(Build.VERSION_CODES.R)
+public suspend fun Class3BiometricOrCredentialAuthPrompt.authenticate(
+    host: AuthPromptHost,
+    crypto: CryptoObject?,
+): AuthenticationResult {
+    return suspendCancellableCoroutine { continuation ->
+        val authPrompt = startAuthentication(
+            host,
+            crypto,
+            Runnable::run,
+            CoroutineAuthPromptCallback(continuation)
+        )
+
+        continuation.invokeOnCancellation {
+            authPrompt.cancelAuthentication()
+        }
+    }
+}
+
+/**
  * Prompts the user to authenticate with a **Class 3** biometric (e.g. fingerprint, face, or iris)
  * or the screen lock credential (i.e. PIN, pattern, or password) for the device.
  *
@@ -39,7 +79,7 @@
  */
 @RequiresApi(Build.VERSION_CODES.R)
 public fun FragmentActivity.startClass3BiometricOrCredentialAuthentication(
-    crypto: BiometricPrompt.CryptoObject?,
+    crypto: CryptoObject?,
     title: CharSequence,
     subtitle: CharSequence? = null,
     description: CharSequence? = null,
@@ -68,6 +108,42 @@
  * @param subtitle An optional subtitle to be displayed on the prompt.
  * @param description An optional description to be displayed on the prompt.
  * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
+ *
+ * @return [AuthenticationResult] for a successful authentication.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class3BiometricOrCredentialAuthPrompt
+ */
+@RequiresApi(Build.VERSION_CODES.R)
+public suspend fun FragmentActivity.authenticateWithClass3BiometricsOrCredentials(
+    crypto: CryptoObject?,
+    title: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): AuthenticationResult {
+    val authPrompt = buildClass3BiometricOrCredentialAuthPrompt(
+        title,
+        subtitle,
+        description,
+        confirmationRequired
+    )
+
+    return authPrompt.authenticate(AuthPromptHost(this), crypto)
+}
+
+/**
+ * Prompts the user to authenticate with a **Class 3** biometric (e.g. fingerprint, face, or iris)
+ * or the screen lock credential (i.e. PIN, pattern, or password) for the device.
+ *
+ * @param crypto A cryptographic object to be associated with this authentication.
+ * @param title The title to be displayed on the prompt.
+ * @param subtitle An optional subtitle to be displayed on the prompt.
+ * @param description An optional description to be displayed on the prompt.
+ * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
  * @param executor An executor for [callback] methods. If `null`, these will run on the main thread.
  * @param callback The object that will receive and process authentication events.
  * @return An [AuthPrompt] handle to the shown prompt.
@@ -76,7 +152,7 @@
  */
 @RequiresApi(Build.VERSION_CODES.R)
 public fun Fragment.startClass3BiometricOrCredentialAuthentication(
-    crypto: BiometricPrompt.CryptoObject?,
+    crypto: CryptoObject?,
     title: CharSequence,
     subtitle: CharSequence? = null,
     description: CharSequence? = null,
@@ -97,13 +173,65 @@
 }
 
 /**
+ * Prompts the user to authenticate with a **Class 3** biometric (e.g. fingerprint, face, or iris)
+ * or the screen lock credential (i.e. PIN, pattern, or password) for the device.
+ *
+ * @param crypto A cryptographic object to be associated with this authentication.
+ * @param title The title to be displayed on the prompt.
+ * @param subtitle An optional subtitle to be displayed on the prompt.
+ * @param description An optional description to be displayed on the prompt.
+ * @param confirmationRequired Whether user confirmation should be required for passive biometrics.
+ *
+ * @return [AuthenticationResult] for a successful authentication.
+ *
+ * @throws AuthPromptErrorException  when an unrecoverable error has been encountered and
+ * authentication has stopped.
+ * @throws AuthPromptFailureException when an authentication attempt by the user has been rejected.
+ *
+ * @see Class3BiometricOrCredentialAuthPrompt
+ */
+@RequiresApi(Build.VERSION_CODES.R)
+public suspend fun Fragment.authenticateWithClass3BiometricsOrCredentials(
+    crypto: CryptoObject?,
+    title: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): AuthenticationResult {
+    val authPrompt = buildClass3BiometricOrCredentialAuthPrompt(
+        title,
+        subtitle,
+        description,
+        confirmationRequired,
+    )
+
+    return authPrompt.authenticate(AuthPromptHost(this), crypto)
+}
+
+/**
+ * Creates a [Class3BiometricOrCredentialAuthPrompt] with the given parameters.
+ */
+private fun buildClass3BiometricOrCredentialAuthPrompt(
+    title: CharSequence,
+    subtitle: CharSequence? = null,
+    description: CharSequence? = null,
+    confirmationRequired: Boolean = true,
+): Class3BiometricOrCredentialAuthPrompt = Class3BiometricOrCredentialAuthPrompt.Builder(title)
+    .apply {
+        subtitle?.let { setSubtitle(it) }
+        description?.let { setDescription(it) }
+        setConfirmationRequired(confirmationRequired)
+    }
+    .build()
+
+/**
  * Creates a [Class3BiometricOrCredentialAuthPrompt] with the given parameters and starts
  * authentication.
  */
 @RequiresApi(Build.VERSION_CODES.R)
 private fun startClass3BiometricOrCredentialAuthenticationInternal(
     host: AuthPromptHost,
-    crypto: BiometricPrompt.CryptoObject?,
+    crypto: CryptoObject?,
     title: CharSequence,
     subtitle: CharSequence? = null,
     description: CharSequence? = null,
@@ -111,15 +239,16 @@
     executor: Executor? = null,
     callback: AuthPromptCallback
 ): AuthPrompt {
-    val prompt = Class3BiometricOrCredentialAuthPrompt.Builder(title).apply {
-        subtitle?.let { setSubtitle(it) }
-        description?.let { setDescription(it) }
-        setConfirmationRequired(confirmationRequired)
-    }.build()
+    val prompt = buildClass3BiometricOrCredentialAuthPrompt(
+        title,
+        subtitle,
+        description,
+        confirmationRequired
+    )
 
     return if (executor == null) {
         prompt.startAuthentication(host, crypto, callback)
     } else {
         prompt.startAuthentication(host, crypto, executor, callback)
     }
-}
\ No newline at end of file
+}
diff --git a/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/CoroutineAuthPromptCallback.kt b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/CoroutineAuthPromptCallback.kt
new file mode 100644
index 0000000..fb6de45
--- /dev/null
+++ b/biometric/biometric-ktx/src/main/java/androidx/biometric/auth/CoroutineAuthPromptCallback.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.biometric.BiometricPrompt.AuthenticationResult
+import androidx.fragment.app.FragmentActivity
+import kotlinx.coroutines.CancellableContinuation
+import kotlin.coroutines.resumeWithException
+
+/**
+ * Implementation of [AuthPromptCallback] used to transform callback results for coroutine APIs.
+ */
+internal class CoroutineAuthPromptCallback(
+    private val continuation: CancellableContinuation<AuthenticationResult>
+) : AuthPromptCallback() {
+    override fun onAuthenticationError(
+        activity: FragmentActivity?,
+        errorCode: Int,
+        errString: CharSequence
+    ) {
+        continuation.resumeWithException(AuthPromptErrorException(errorCode, errString))
+    }
+
+    override fun onAuthenticationSucceeded(
+        activity: FragmentActivity?,
+        result: AuthenticationResult
+    ) {
+        continuation.resumeWith(Result.success(result))
+    }
+
+    override fun onAuthenticationFailed(activity: FragmentActivity?) {
+        continuation.resumeWithException(AuthPromptFailureException())
+    }
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/BiometricManager.java b/biometric/biometric/src/main/java/androidx/biometric/BiometricManager.java
index 4f83a98..c126640 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/BiometricManager.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/BiometricManager.java
@@ -371,14 +371,12 @@
             return BIOMETRIC_ERROR_NO_HARDWARE;
         }
 
-        // No authenticators are enrolled if the device is not secured.
-        if (!mInjector.isDeviceSecuredWithCredential()) {
-            return BIOMETRIC_ERROR_NONE_ENROLLED;
-        }
-
-        // Credential authentication is always possible if the device is secured.
+        // Credential authentication is always possible if the device is secured. Conversely, no
+        // form of authentication is possible if the device is not secured.
         if (AuthenticatorUtils.isDeviceCredentialAllowed(authenticators)) {
-            return BIOMETRIC_SUCCESS;
+            return mInjector.isDeviceSecuredWithCredential()
+                    ? BIOMETRIC_SUCCESS
+                    : BIOMETRIC_ERROR_NONE_ENROLLED;
         }
 
         // The class of some non-fingerprint biometrics can be checked on API 29.
@@ -393,7 +391,7 @@
             // Having fingerprint hardware is a prerequisite, since BiometricPrompt internally
             // calls FingerprintManager#getErrorString() on API 28 (b/151443237).
             return mInjector.isFingerprintHardwarePresent()
-                    ? canAuthenticateWithFingerprintOrUnknown()
+                    ? canAuthenticateWithFingerprintOrUnknownBiometric()
                     : BIOMETRIC_ERROR_NO_HARDWARE;
         }
 
@@ -443,7 +441,7 @@
         }
 
         // If all else fails, check if fingerprint authentication is available.
-        return canAuthenticateWithFingerprintOrUnknown();
+        return canAuthenticateWithFingerprintOrUnknownBiometric();
     }
 
     /**
@@ -465,14 +463,23 @@
     }
 
     /**
-     * Checks if the user can authenticate with fingerprint, falling back to
-     * {@link #BIOMETRIC_STATUS_UNKNOWN} for any error condition.
+     * Checks if the user can authenticate with fingerprint or with a biometric sensor for which
+     * there is no platform method to check availability.
      *
-     * @return {@link #BIOMETRIC_SUCCESS} if the user can authenticate with fingerprint, or
-     * {@link #BIOMETRIC_STATUS_UNKNOWN} otherwise.
+     * @return {@link #BIOMETRIC_SUCCESS} if the user can authenticate with fingerprint. Otherwise,
+     * returns an error code indicating why the user can't authenticate, or
+     * {@link #BIOMETRIC_STATUS_UNKNOWN} if it is unknown whether the user can authenticate.
      */
     @AuthenticationStatus
-    private int canAuthenticateWithFingerprintOrUnknown() {
+    private int canAuthenticateWithFingerprintOrUnknownBiometric() {
+        // If the device is not secured, authentication is definitely not possible. Use
+        // FingerprintManager to distinguish between the "no hardware" and "none enrolled" cases.
+        if (!mInjector.isDeviceSecuredWithCredential()) {
+            return canAuthenticateWithFingerprint();
+        }
+
+        // Check for definite availability of fingerprint. Otherwise, return "unknown" to allow for
+        // non-fingerprint biometrics (e.g. iris) that may be available via BiometricPrompt.
         return canAuthenticateWithFingerprint() == BIOMETRIC_SUCCESS
                 ? BIOMETRIC_SUCCESS
                 : BIOMETRIC_STATUS_UNKNOWN;
diff --git a/biometric/biometric/src/test/java/androidx/biometric/BiometricManagerTest.java b/biometric/biometric/src/test/java/androidx/biometric/BiometricManagerTest.java
index abbd6f0..d359008 100644
--- a/biometric/biometric/src/test/java/androidx/biometric/BiometricManagerTest.java
+++ b/biometric/biometric/src/test/java/androidx/biometric/BiometricManagerTest.java
@@ -240,39 +240,78 @@
 
     @Test
     @Config(minSdk = Build.VERSION_CODES.Q, maxSdk = Build.VERSION_CODES.Q)
-    public void testCanAuthenticate_ReturnsError_WhenDeviceNotSecured_OnApi29() {
+    public void testCanAuthenticate_ReturnsError_WhenUnsecured_BiometricOnly_OnApi29() {
         final android.hardware.biometrics.BiometricManager frameworkBiometricManager =
                 mock(android.hardware.biometrics.BiometricManager.class);
-        when(frameworkBiometricManager.canAuthenticate()).thenReturn(BIOMETRIC_SUCCESS);
-        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
-        when(mFingerprintManager.hasEnrolledFingerprints()).thenReturn(true);
+        when(frameworkBiometricManager.canAuthenticate()).thenReturn(BIOMETRIC_ERROR_NO_HARDWARE);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+        when(mFingerprintManager.hasEnrolledFingerprints()).thenReturn(false);
 
         final BiometricManager.Injector injector = new TestInjector.Builder()
                 .setBiometricManager(frameworkBiometricManager)
                 .setDeviceSecurable(true)
-                .setFingerprintHardwarePresent(true)
+                .setFingerprintHardwarePresent(false)
                 .build();
 
         final BiometricManager biometricManager = new BiometricManager(injector);
         final int authenticators = Authenticators.BIOMETRIC_WEAK;
         assertThat(biometricManager.canAuthenticate(authenticators))
+                .isEqualTo(BIOMETRIC_ERROR_NO_HARDWARE);
+    }
+
+    @Test
+    @Config(maxSdk = Build.VERSION_CODES.P)
+    public void testCanAuthenticate_ReturnsError_WhenUnsecured_BiometricOnly_OnApi28AndBelow() {
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+        when(mFingerprintManager.hasEnrolledFingerprints()).thenReturn(false);
+
+        final BiometricManager.Injector injector = new TestInjector.Builder()
+                .setFingerprintManager(mFingerprintManager)
+                .setDeviceSecurable(true)
+                .setFingerprintHardwarePresent(false)
+                .build();
+
+        final BiometricManager biometricManager = new BiometricManager(injector);
+        final int authenticators = Authenticators.BIOMETRIC_WEAK;
+        assertThat(biometricManager.canAuthenticate(authenticators))
+                .isEqualTo(BIOMETRIC_ERROR_NO_HARDWARE);
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.Q, maxSdk = Build.VERSION_CODES.Q)
+    public void testCanAuthenticate_ReturnsError_WhenUnsecured_CredentialAllowed_OnApi29() {
+        final android.hardware.biometrics.BiometricManager frameworkBiometricManager =
+                mock(android.hardware.biometrics.BiometricManager.class);
+        when(frameworkBiometricManager.canAuthenticate()).thenReturn(BIOMETRIC_ERROR_NO_HARDWARE);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+        when(mFingerprintManager.hasEnrolledFingerprints()).thenReturn(false);
+
+        final BiometricManager.Injector injector = new TestInjector.Builder()
+                .setBiometricManager(frameworkBiometricManager)
+                .setDeviceSecurable(true)
+                .setFingerprintHardwarePresent(false)
+                .build();
+
+        final BiometricManager biometricManager = new BiometricManager(injector);
+        final int authenticators = Authenticators.BIOMETRIC_WEAK | Authenticators.DEVICE_CREDENTIAL;
+        assertThat(biometricManager.canAuthenticate(authenticators))
                 .isEqualTo(BIOMETRIC_ERROR_NONE_ENROLLED);
     }
 
     @Test
     @Config(maxSdk = Build.VERSION_CODES.P)
-    public void testCanAuthenticate_ReturnsError_WhenDeviceNotSecured_OnApi28AndBelow() {
-        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
-        when(mFingerprintManager.hasEnrolledFingerprints()).thenReturn(true);
+    public void testCanAuthenticate_ReturnsError_WhenUnsecured_CredentialAllowed_OnApi28AndBelow() {
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+        when(mFingerprintManager.hasEnrolledFingerprints()).thenReturn(false);
 
         final BiometricManager.Injector injector = new TestInjector.Builder()
                 .setFingerprintManager(mFingerprintManager)
                 .setDeviceSecurable(true)
-                .setFingerprintHardwarePresent(true)
+                .setFingerprintHardwarePresent(false)
                 .build();
 
         final BiometricManager biometricManager = new BiometricManager(injector);
-        final int authenticators = Authenticators.BIOMETRIC_WEAK;
+        final int authenticators = Authenticators.BIOMETRIC_WEAK | Authenticators.DEVICE_CREDENTIAL;
         assertThat(biometricManager.canAuthenticate(authenticators))
                 .isEqualTo(BIOMETRIC_ERROR_NONE_ENROLLED);
     }
diff --git a/biometric/integration-tests/testapp/src/main/java/androidx/biometric/integration/testapp/AuthPromptTestActivity.kt b/biometric/integration-tests/testapp/src/main/java/androidx/biometric/integration/testapp/AuthPromptTestActivity.kt
index 896f04e8..0e92e2a 100755
--- a/biometric/integration-tests/testapp/src/main/java/androidx/biometric/integration/testapp/AuthPromptTestActivity.kt
+++ b/biometric/integration-tests/testapp/src/main/java/androidx/biometric/integration/testapp/AuthPromptTestActivity.kt
@@ -26,7 +26,7 @@
 import androidx.biometric.auth.AuthPromptCallback
 import androidx.biometric.auth.startClass2BiometricAuthentication
 import androidx.biometric.auth.startClass2BiometricOrCredentialAuthentication
-import androidx.biometric.auth.startClass3BiometricAuthentication
+import androidx.biometric.auth.authenticateWithClass3Biometrics
 import androidx.biometric.auth.startClass3BiometricOrCredentialAuthentication
 import androidx.biometric.auth.startCredentialAuthentication
 import androidx.biometric.integration.testapp.R.string.biometric_prompt_description
@@ -191,7 +191,7 @@
                 )
 
             R.id.class3_biometric_button ->
-                startClass3BiometricAuthentication(
+                authenticateWithClass3Biometrics(
                     crypto = createCryptoOrNull(),
                     title = title,
                     subtitle = subtitle,
diff --git a/biometric/settings.gradle b/biometric/settings.gradle
index 697db78..7a96db8 100644
--- a/biometric/settings.gradle
+++ b/biometric/settings.gradle
@@ -20,6 +20,7 @@
 setupPlayground(this, "..")
 selectProjectsFromAndroidX({ name ->
     if (name.startsWith(":biometric")) return true
+    if (name == ":annotation:annotation-sampled") return true
     return false
 })
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index fbea9d9..29e6fb6 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -124,19 +124,19 @@
     val VERSIONED_PARCELABLE = Version("1.2.0-alpha01")
     val VIEWPAGER = Version("1.1.0-alpha01")
     val VIEWPAGER2 = Version("1.1.0-alpha02")
-    val WEAR = Version("1.2.0-alpha05")
-    val WEAR_COMPLICATIONS = Version("1.0.0-alpha05")
+    val WEAR = Version("1.2.0-alpha06")
+    val WEAR_COMPLICATIONS = Version("1.0.0-alpha06")
     val WEAR_INPUT = Version("1.1.0-alpha01")
     val WEAR_ONGOING = Version("1.0.0-alpha01")
     val WEAR_PHONE_INTERACTIONS = Version("1.0.0-alpha01")
     val WEAR_REMOTE_INTERACTIONS = Version("1.0.0-alpha01")
     val WEAR_TILES = Version("1.0.0-alpha01")
     val WEAR_TILES_DATA = WEAR_TILES
-    val WEAR_WATCHFACE = Version("1.0.0-alpha05")
-    val WEAR_WATCHFACE_CLIENT = Version("1.0.0-alpha05")
-    val WEAR_WATCHFACE_DATA = Version("1.0.0-alpha05")
-    val WEAR_WATCHFACE_EDITOR = Version("1.0.0-alpha05")
-    val WEAR_WATCHFACE_STYLE = Version("1.0.0-alpha05")
+    val WEAR_WATCHFACE = Version("1.0.0-alpha06")
+    val WEAR_WATCHFACE_CLIENT = Version("1.0.0-alpha06")
+    val WEAR_WATCHFACE_DATA = Version("1.0.0-alpha06")
+    val WEAR_WATCHFACE_EDITOR = Version("1.0.0-alpha06")
+    val WEAR_WATCHFACE_STYLE = Version("1.0.0-alpha06")
     val WEBKIT = Version("1.5.0-alpha01")
     val WINDOW = Version("1.0.0-alpha02")
     val WINDOW_EXTENSIONS = Version("1.0.0-alpha01")
diff --git a/car/app/app-aaos/api/current.txt b/car/app/app-aaos/api/current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/car/app/app-aaos/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/car/app/app-aaos/api/public_plus_experimental_current.txt b/car/app/app-aaos/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/car/app/app-aaos/api/public_plus_experimental_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/car/app/app-aaos/api/res-current.txt b/car/app/app-aaos/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/car/app/app-aaos/api/res-current.txt
diff --git a/car/app/app-aaos/api/restricted_current.txt b/car/app/app-aaos/api/restricted_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/car/app/app-aaos/api/restricted_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/car/app/app-aaos/build.gradle b/car/app/app-aaos/build.gradle
new file mode 100644
index 0000000..87a2082
--- /dev/null
+++ b/car/app/app-aaos/build.gradle
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import androidx.build.LibraryGroups
+import androidx.build.LibraryType
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+}
+
+dependencies {
+    // Add dependencies here
+}
+
+androidx {
+    name = "Android for Cars App Library AAOS Extension"
+    type = LibraryType.PUBLISHED_LIBRARY
+    mavenGroup = LibraryGroups.CAR_APP
+    inceptionYear = "2021"
+    description = "AAOS specific funationaltiy to build navigation, parking, and charging apps for cars"
+}
diff --git a/car/app/app-aaos/lint-baseline.xml b/car/app/app-aaos/lint-baseline.xml
new file mode 100644
index 0000000..8f1aa4b
--- /dev/null
+++ b/car/app/app-aaos/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.2.0-beta02" client="gradle" variant="debug" version="4.2.0-beta02">
+
+</issues>
diff --git a/car/app/app-aaos/src/androidTest/AndroidManifest.xml b/car/app/app-aaos/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..3a56865
--- /dev/null
+++ b/car/app/app-aaos/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.car.app.aaos.test">
+
+</manifest>
diff --git a/car/app/app-aaos/src/main/AndroidManifest.xml b/car/app/app-aaos/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c562b9f
--- /dev/null
+++ b/car/app/app-aaos/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.car.app.aaos">
+
+</manifest>
\ No newline at end of file
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 6f4f9e1..98f0298 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -172,7 +172,7 @@
     method public androidx.car.app.model.CarText? getTitle();
     method public int getType();
     method public boolean isStandard();
-    method public androidx.car.app.model.Action.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.Action.Builder newBuilder();
     method public static String typeToString(int);
     field public static final androidx.car.app.model.Action APP_ICON;
     field public static final androidx.car.app.model.Action BACK;
@@ -236,7 +236,7 @@
     method public androidx.core.graphics.drawable.IconCompat? getIcon();
     method public androidx.car.app.model.CarColor? getTint();
     method public int getType();
-    method public androidx.car.app.model.CarIcon.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.CarIcon.Builder newBuilder();
     field public static final androidx.car.app.model.CarIcon ALERT;
     field public static final androidx.car.app.model.CarIcon APP_ICON;
     field public static final androidx.car.app.model.CarIcon BACK;
@@ -251,6 +251,7 @@
 
   public static final class CarIcon.Builder {
     ctor public CarIcon.Builder(androidx.core.graphics.drawable.IconCompat);
+    ctor public CarIcon.Builder(androidx.car.app.model.CarIcon);
     method public androidx.car.app.model.CarIcon build();
     method public androidx.car.app.model.CarIcon.Builder setTint(androidx.car.app.model.CarColor?);
   }
@@ -446,24 +447,25 @@
     method public androidx.car.app.model.MessageTemplate build();
     method public androidx.car.app.model.MessageTemplate.Builder setActionList(java.util.List<androidx.car.app.model.Action!>);
     method @Deprecated public androidx.car.app.model.MessageTemplate.Builder setActions(java.util.List<androidx.car.app.model.Action!>);
-    method public androidx.car.app.model.MessageTemplate.Builder setDebugCause(Throwable?);
+    method @Deprecated public androidx.car.app.model.MessageTemplate.Builder setDebugCause(Throwable?);
+    method public androidx.car.app.model.MessageTemplate.Builder setDebugMessage(Throwable?);
     method public androidx.car.app.model.MessageTemplate.Builder setDebugMessage(String?);
     method public androidx.car.app.model.MessageTemplate.Builder setHeaderAction(androidx.car.app.model.Action?);
     method public androidx.car.app.model.MessageTemplate.Builder setIcon(androidx.car.app.model.CarIcon?);
-    method public androidx.car.app.model.MessageTemplate.Builder setMessage(CharSequence);
     method public androidx.car.app.model.MessageTemplate.Builder setTitle(CharSequence?);
   }
 
   public class Metadata {
     method public static androidx.car.app.model.Metadata.Builder builder();
     method public androidx.car.app.model.Place? getPlace();
-    method public androidx.car.app.model.Metadata.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.Metadata.Builder newBuilder();
     method public static androidx.car.app.model.Metadata ofPlace(androidx.car.app.model.Place);
     field public static final androidx.car.app.model.Metadata EMPTY_METADATA;
   }
 
   public static final class Metadata.Builder {
     ctor public Metadata.Builder();
+    ctor public Metadata.Builder(androidx.car.app.model.Metadata);
     method public androidx.car.app.model.Metadata build();
     method public androidx.car.app.model.Metadata.Builder setPlace(androidx.car.app.model.Place?);
   }
@@ -532,11 +534,12 @@
     method public static androidx.car.app.model.Place.Builder builder(androidx.car.app.model.LatLng);
     method public androidx.car.app.model.LatLng getLatLng();
     method public androidx.car.app.model.PlaceMarker? getMarker();
-    method public androidx.car.app.model.Place.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.Place.Builder newBuilder();
   }
 
   public static final class Place.Builder {
     ctor public Place.Builder(androidx.car.app.model.LatLng);
+    ctor public Place.Builder(androidx.car.app.model.Place);
     method public androidx.car.app.model.Place build();
     method public androidx.car.app.model.Place.Builder setLatLng(androidx.car.app.model.LatLng);
     method public androidx.car.app.model.Place.Builder setMarker(androidx.car.app.model.PlaceMarker?);
@@ -929,7 +932,6 @@
     method public androidx.car.app.model.CarIcon? getLanesImage();
     method public androidx.car.app.navigation.model.Maneuver? getManeuver();
     method public androidx.car.app.model.CarText? getRoad();
-    method public androidx.car.app.navigation.model.Step.Builder newBuilder();
   }
 
   public static final class Step.Builder {
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 6f4f9e1..98f0298 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -172,7 +172,7 @@
     method public androidx.car.app.model.CarText? getTitle();
     method public int getType();
     method public boolean isStandard();
-    method public androidx.car.app.model.Action.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.Action.Builder newBuilder();
     method public static String typeToString(int);
     field public static final androidx.car.app.model.Action APP_ICON;
     field public static final androidx.car.app.model.Action BACK;
@@ -236,7 +236,7 @@
     method public androidx.core.graphics.drawable.IconCompat? getIcon();
     method public androidx.car.app.model.CarColor? getTint();
     method public int getType();
-    method public androidx.car.app.model.CarIcon.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.CarIcon.Builder newBuilder();
     field public static final androidx.car.app.model.CarIcon ALERT;
     field public static final androidx.car.app.model.CarIcon APP_ICON;
     field public static final androidx.car.app.model.CarIcon BACK;
@@ -251,6 +251,7 @@
 
   public static final class CarIcon.Builder {
     ctor public CarIcon.Builder(androidx.core.graphics.drawable.IconCompat);
+    ctor public CarIcon.Builder(androidx.car.app.model.CarIcon);
     method public androidx.car.app.model.CarIcon build();
     method public androidx.car.app.model.CarIcon.Builder setTint(androidx.car.app.model.CarColor?);
   }
@@ -446,24 +447,25 @@
     method public androidx.car.app.model.MessageTemplate build();
     method public androidx.car.app.model.MessageTemplate.Builder setActionList(java.util.List<androidx.car.app.model.Action!>);
     method @Deprecated public androidx.car.app.model.MessageTemplate.Builder setActions(java.util.List<androidx.car.app.model.Action!>);
-    method public androidx.car.app.model.MessageTemplate.Builder setDebugCause(Throwable?);
+    method @Deprecated public androidx.car.app.model.MessageTemplate.Builder setDebugCause(Throwable?);
+    method public androidx.car.app.model.MessageTemplate.Builder setDebugMessage(Throwable?);
     method public androidx.car.app.model.MessageTemplate.Builder setDebugMessage(String?);
     method public androidx.car.app.model.MessageTemplate.Builder setHeaderAction(androidx.car.app.model.Action?);
     method public androidx.car.app.model.MessageTemplate.Builder setIcon(androidx.car.app.model.CarIcon?);
-    method public androidx.car.app.model.MessageTemplate.Builder setMessage(CharSequence);
     method public androidx.car.app.model.MessageTemplate.Builder setTitle(CharSequence?);
   }
 
   public class Metadata {
     method public static androidx.car.app.model.Metadata.Builder builder();
     method public androidx.car.app.model.Place? getPlace();
-    method public androidx.car.app.model.Metadata.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.Metadata.Builder newBuilder();
     method public static androidx.car.app.model.Metadata ofPlace(androidx.car.app.model.Place);
     field public static final androidx.car.app.model.Metadata EMPTY_METADATA;
   }
 
   public static final class Metadata.Builder {
     ctor public Metadata.Builder();
+    ctor public Metadata.Builder(androidx.car.app.model.Metadata);
     method public androidx.car.app.model.Metadata build();
     method public androidx.car.app.model.Metadata.Builder setPlace(androidx.car.app.model.Place?);
   }
@@ -532,11 +534,12 @@
     method public static androidx.car.app.model.Place.Builder builder(androidx.car.app.model.LatLng);
     method public androidx.car.app.model.LatLng getLatLng();
     method public androidx.car.app.model.PlaceMarker? getMarker();
-    method public androidx.car.app.model.Place.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.Place.Builder newBuilder();
   }
 
   public static final class Place.Builder {
     ctor public Place.Builder(androidx.car.app.model.LatLng);
+    ctor public Place.Builder(androidx.car.app.model.Place);
     method public androidx.car.app.model.Place build();
     method public androidx.car.app.model.Place.Builder setLatLng(androidx.car.app.model.LatLng);
     method public androidx.car.app.model.Place.Builder setMarker(androidx.car.app.model.PlaceMarker?);
@@ -929,7 +932,6 @@
     method public androidx.car.app.model.CarIcon? getLanesImage();
     method public androidx.car.app.navigation.model.Maneuver? getManeuver();
     method public androidx.car.app.model.CarText? getRoad();
-    method public androidx.car.app.navigation.model.Step.Builder newBuilder();
   }
 
   public static final class Step.Builder {
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 6f4f9e1..98f0298 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -172,7 +172,7 @@
     method public androidx.car.app.model.CarText? getTitle();
     method public int getType();
     method public boolean isStandard();
-    method public androidx.car.app.model.Action.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.Action.Builder newBuilder();
     method public static String typeToString(int);
     field public static final androidx.car.app.model.Action APP_ICON;
     field public static final androidx.car.app.model.Action BACK;
@@ -236,7 +236,7 @@
     method public androidx.core.graphics.drawable.IconCompat? getIcon();
     method public androidx.car.app.model.CarColor? getTint();
     method public int getType();
-    method public androidx.car.app.model.CarIcon.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.CarIcon.Builder newBuilder();
     field public static final androidx.car.app.model.CarIcon ALERT;
     field public static final androidx.car.app.model.CarIcon APP_ICON;
     field public static final androidx.car.app.model.CarIcon BACK;
@@ -251,6 +251,7 @@
 
   public static final class CarIcon.Builder {
     ctor public CarIcon.Builder(androidx.core.graphics.drawable.IconCompat);
+    ctor public CarIcon.Builder(androidx.car.app.model.CarIcon);
     method public androidx.car.app.model.CarIcon build();
     method public androidx.car.app.model.CarIcon.Builder setTint(androidx.car.app.model.CarColor?);
   }
@@ -446,24 +447,25 @@
     method public androidx.car.app.model.MessageTemplate build();
     method public androidx.car.app.model.MessageTemplate.Builder setActionList(java.util.List<androidx.car.app.model.Action!>);
     method @Deprecated public androidx.car.app.model.MessageTemplate.Builder setActions(java.util.List<androidx.car.app.model.Action!>);
-    method public androidx.car.app.model.MessageTemplate.Builder setDebugCause(Throwable?);
+    method @Deprecated public androidx.car.app.model.MessageTemplate.Builder setDebugCause(Throwable?);
+    method public androidx.car.app.model.MessageTemplate.Builder setDebugMessage(Throwable?);
     method public androidx.car.app.model.MessageTemplate.Builder setDebugMessage(String?);
     method public androidx.car.app.model.MessageTemplate.Builder setHeaderAction(androidx.car.app.model.Action?);
     method public androidx.car.app.model.MessageTemplate.Builder setIcon(androidx.car.app.model.CarIcon?);
-    method public androidx.car.app.model.MessageTemplate.Builder setMessage(CharSequence);
     method public androidx.car.app.model.MessageTemplate.Builder setTitle(CharSequence?);
   }
 
   public class Metadata {
     method public static androidx.car.app.model.Metadata.Builder builder();
     method public androidx.car.app.model.Place? getPlace();
-    method public androidx.car.app.model.Metadata.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.Metadata.Builder newBuilder();
     method public static androidx.car.app.model.Metadata ofPlace(androidx.car.app.model.Place);
     field public static final androidx.car.app.model.Metadata EMPTY_METADATA;
   }
 
   public static final class Metadata.Builder {
     ctor public Metadata.Builder();
+    ctor public Metadata.Builder(androidx.car.app.model.Metadata);
     method public androidx.car.app.model.Metadata build();
     method public androidx.car.app.model.Metadata.Builder setPlace(androidx.car.app.model.Place?);
   }
@@ -532,11 +534,12 @@
     method public static androidx.car.app.model.Place.Builder builder(androidx.car.app.model.LatLng);
     method public androidx.car.app.model.LatLng getLatLng();
     method public androidx.car.app.model.PlaceMarker? getMarker();
-    method public androidx.car.app.model.Place.Builder newBuilder();
+    method @Deprecated public androidx.car.app.model.Place.Builder newBuilder();
   }
 
   public static final class Place.Builder {
     ctor public Place.Builder(androidx.car.app.model.LatLng);
+    ctor public Place.Builder(androidx.car.app.model.Place);
     method public androidx.car.app.model.Place build();
     method public androidx.car.app.model.Place.Builder setLatLng(androidx.car.app.model.LatLng);
     method public androidx.car.app.model.Place.Builder setMarker(androidx.car.app.model.PlaceMarker?);
@@ -929,7 +932,6 @@
     method public androidx.car.app.model.CarIcon? getLanesImage();
     method public androidx.car.app.navigation.model.Maneuver? getManeuver();
     method public androidx.car.app.model.CarText? getRoad();
-    method public androidx.car.app.navigation.model.Step.Builder newBuilder();
   }
 
   public static final class Step.Builder {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Action.java b/car/app/app/src/main/java/androidx/car/app/model/Action.java
index 3989dad..35d2146 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Action.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Action.java
@@ -144,8 +144,12 @@
     /**
      * Returns a {@link Builder} instance configured with the same data as this {@link Action}
      * instance.
+     *
+     * @deprecated use constructor.
      */
+    // TODO(b/177484889): remove once host is changed to use new public ctor.
     @NonNull
+    @Deprecated
     public Builder newBuilder() {
         return new Builder(this);
     }
@@ -405,7 +409,13 @@
         public Builder() {
         }
 
-        Builder(Action action) {
+        /**
+         * Returns a {@link Builder} instance configured with the same data as the given
+         * {@link Action} instance.
+         *
+         * @throws NullPointerException if {@code icon} is {@code null}.
+         */
+        Builder(@NonNull Action action) {
             mTitle = action.getTitle();
             mIcon = action.getIcon();
             mBackgroundColor = action.getBackgroundColor();
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
index eb1b30e..b858168 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
@@ -21,6 +21,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.SuppressLint;
 import android.content.ContentResolver;
 import android.graphics.PorterDuff.Mode;
 import android.os.Build.VERSION;
@@ -105,6 +106,7 @@
      */
     // TODO(shiufai): investigate how to expose IntDefs if needed.
     @RestrictTo(LIBRARY)
+    @SuppressLint("UniqueConstants") // TYPE_APP will be removed in a follow-up change.
     @IntDef(
             value = {
                     TYPE_CUSTOM,
@@ -210,7 +212,10 @@
     /**
      * Returns a {@link Builder} instance configured with the same data as this {@link CarIcon}
      * instance.
+     * @deprecated use constructor.
      */
+    // TODO(b/177484889): remove once host is changed to use new public ctor.
+    @Deprecated
     @NonNull
     public Builder newBuilder() {
         return new Builder(this);
@@ -425,7 +430,14 @@
             mTint = null;
         }
 
-        Builder(@NonNull CarIcon carIcon) {
+        /**
+         * Returns a {@link Builder} instance configured with the same data as the given
+         * {@link CarIcon} instance.
+         *
+         * @throws NullPointerException if {@code icon} is {@code null}.
+         */
+        public Builder(@NonNull CarIcon carIcon) {
+            requireNonNull(carIcon);
             mType = carIcon.getType();
             mIcon = carIcon.getIcon();
             mTint = carIcon.getTint();
diff --git a/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
index c899dd7..10abbed 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
@@ -175,8 +175,7 @@
 
         /**
          * Sets the {@link CharSequence} to show as the template's title, or {@code null} to not
-         * show a
-         * title.
+         * show a title.
          */
         @NonNull
         public Builder setTitle(@Nullable CharSequence title) {
@@ -185,44 +184,51 @@
         }
 
         /**
-         * Sets the {@link CharSequence} to display as the message in the template.
-         *
-         * @throws NullPointerException if {@code message} is null.
-         */
-        @NonNull
-        public Builder setMessage(@NonNull CharSequence message) {
-            this.mMessage = CarText.create(requireNonNull(message));
-            return this;
-        }
-
-        /**
          * Sets a {@link Throwable} for debugging purposes, or {@code null} to not show it.
          *
          * <p>The cause will be displayed along with the message set in {@link #setDebugMessage}.
          *
          * <p>The host may choose to not display this debugging information if it doesn't deem it
          * appropriate, for example, when running on a production environment rather than in a
-         * simulator
-         * such as the Desktop Head Unit.
+         * simulator such as the Desktop Head Unit.
+         *
+         * @deprecated use {@link #setDebugMessage(String) instead.}
          */
         @NonNull
         // Suppress as the cause is transformed into a message before transport.
         @SuppressLint("MissingGetterMatchingBuilder")
+        @Deprecated
+        // TODO(b/177591352): remove once host does not reference this method.
         public Builder setDebugCause(@Nullable Throwable cause) {
             this.mDebugCause = cause;
             return this;
         }
 
         /**
+         * Sets a {@link Throwable} for debugging purposes, or {@code null} to not show it.
+         *
+         * <p>The cause will be displayed along with the message set in
+         * {@link #setDebugMessage(String)}.
+         *
+         * <p>The host may choose to not display this debugging information if it doesn't deem it
+         * appropriate, for example, when running on a production environment rather than in a
+         * simulator such as the Desktop Head Unit.
+         */
+        @NonNull
+        public Builder setDebugMessage(@Nullable Throwable cause) {
+            this.mDebugCause = cause;
+            return this;
+        }
+
+        /**
          * Sets a debug message for debugging purposes, or {@code null} to not show a debug message.
          *
          * <p>The debug message will be displayed along with the cause set in
-         * {@link #setDebugCause}.
+         * {@link #setDebugMessage}.
          *
          * <p>The host may choose to not display this debugging information if it doesn't deem it
          * appropriate, for example, when running on a production environment rather than in a
-         * simulator
-         * such as the Desktop Head Unit.
+         * simulator such as the Desktop Head Unit.
          */
         @NonNull
         public Builder setDebugMessage(@Nullable String debugMessage) {
@@ -237,14 +243,11 @@
          * <h4>Icon Sizing Guidance</h4>
          *
          * The provided icon should have a maximum size of 64 x 64 dp. If the icon exceeds this
-         * maximum
-         * size in either one of the dimensions, it will be scaled down and centered inside the
-         * bounding
-         * box while preserving the aspect ratio.
+         * maximum size in either one of the dimensions, it will be scaled down and centered
+         * inside the bounding box while preserving the aspect ratio.
          *
          * <p>See {@link CarIcon} for more details related to providing icon and image resources
-         * that
-         * work with different car screen pixel densities.
+         * that work with different car screen pixel densities.
          */
         @NonNull
         public Builder setIcon(@Nullable CarIcon icon) {
@@ -255,8 +258,7 @@
 
         /**
          * Sets the {@link Action} that will be displayed in the header of the template, or
-         * {@code null}
-         * to not display an action.
+         * {@code null} to not display an action.
          *
          * <h4>Requirements</h4>
          *
@@ -315,15 +317,13 @@
          *
          * <h4>Requirements</h4>
          *
-         * A non-empty message must be set on the template with {@link
-         * Builder#setMessage(CharSequence)}.
+         * A non-empty message must be set on the template.
          *
          * <p>Either a header {@link Action} or title must be set on the template.
          *
          * @throws IllegalStateException if the message is empty.
          * @throws IllegalStateException if the template does not have either a title or header
-         *                               {@link
-         *                               Action} set.
+         *                               {@link Action} set.
          */
         @NonNull
         public MessageTemplate build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Metadata.java b/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
index f5e152b..8894213 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
@@ -51,7 +51,12 @@
         return new Builder().setPlace(requireNonNull(place)).build();
     }
 
-    /** Returns a new {@link Builder} with the data from this {@link Metadata} instance. */
+    /**
+     * Returns a new {@link Builder} with the data from this {@link Metadata} instance.
+     * @deprecated use constructor.
+     */
+    // TODO(b/177484889): remove once host is changed to use new public ctor.
+    @Deprecated
     @NonNull
     public Builder newBuilder() {
         return new Builder(this);
@@ -117,8 +122,13 @@
         public Builder() {
         }
 
-        Builder(Metadata metadata) {
-            this.mPlace = metadata.getPlace();
+        /**
+         * Returns a new {@link Builder} with the data from the given {@link Metadata} instance.
+         *
+         * @throws NullPointerException if {@code icon} is {@code null}.
+         */
+        public Builder(@NonNull Metadata metadata) {
+            this.mPlace = requireNonNull(metadata).getPlace();
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Place.java b/car/app/app/src/main/java/androidx/car/app/model/Place.java
index 2932e24..771f4f6 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Place.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Place.java
@@ -45,8 +45,13 @@
         return new Builder(requireNonNull(latLng));
     }
 
-    /** Returns a {@link Builder} instance with the same data as this {@link Place} instance. */
+    /**
+     * Returns a {@link Builder} instance with the same data as this {@link Place} instance.
+     * @deprecated use constructor.
+     */
+    // TODO(b/177484889): remove once host is changed to use new public ctor.
     @NonNull
+    @Deprecated
     public Builder newBuilder() {
         return new Builder(this);
     }
@@ -113,7 +118,12 @@
             mLatLng = latLng;
         }
 
-        Builder(Place place) {
+        /**
+         * Returns a {@link Builder} instance with the same data as the given {@link Place}
+         * instance.
+         */
+        public Builder(@NonNull Place place) {
+            requireNonNull(place);
             mLatLng = place.getLatLng();
             mMarker = place.getMarker();
         }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
index 8d9c01d..0745a1b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
@@ -19,6 +19,8 @@
 
 import static androidx.annotation.RestrictTo.Scope;
 
+import static java.util.Objects.requireNonNull;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
@@ -59,13 +61,13 @@
      */
     @NonNull
     public static final ActionsConstraints ACTIONS_CONSTRAINTS_SIMPLE =
-            ACTIONS_CONSTRAINTS_CONSERVATIVE.newBuilder().setMaxCustomTitles(1).build();
+            new ActionsConstraints.Builder(ACTIONS_CONSTRAINTS_CONSERVATIVE).setMaxCustomTitles(
+                    1).build();
 
     /** Constraints for navigation templates. */
     @NonNull
     public static final ActionsConstraints ACTIONS_CONSTRAINTS_NAVIGATION =
-            ACTIONS_CONSTRAINTS_CONSERVATIVE
-                    .newBuilder()
+            new ActionsConstraints.Builder(ACTIONS_CONSTRAINTS_CONSERVATIVE)
                     .setMaxActions(4)
                     .setMaxCustomTitles(1)
                     .addRequiredActionType(Action.TYPE_CUSTOM)
@@ -84,16 +86,6 @@
         return new Builder();
     }
 
-    /**
-     * Returns a new builder that contains the same data as this {@link ActionsConstraints}
-     * instance.
-     */
-    @VisibleForTesting
-    @NonNull
-    public Builder newBuilder() {
-        return new Builder(this);
-    }
-
     /** Returns the max number of actions allowed. */
     public int getMaxActions() {
         return mMaxActions;
@@ -239,7 +231,14 @@
         public Builder() {
         }
 
-        Builder(ActionsConstraints constraints) {
+        /**
+         * Returns a new builder that contains the same data as the given {@link ActionsConstraints}
+         * instance.
+         *
+         * @throws NullPointerException if {@code latLng} is {@code null}.
+         */
+        public Builder(@NonNull ActionsConstraints constraints) {
+            requireNonNull(constraints);
             this.mMaxActions = constraints.getMaxActions();
             this.mMaxCustomTitles = constraints.getMaxCustomTitles();
             this.mRequiredActionTypes.addAll(constraints.getRequiredActionTypes());
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
index 685cf14..e88c148 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
@@ -16,6 +16,8 @@
 
 package androidx.car.app.model.constraints;
 
+import static java.util.Objects.requireNonNull;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.car.app.model.CarIcon;
@@ -67,7 +69,7 @@
     /** The constraints for a full-width row in a list (simple + toggle support). */
     @NonNull
     public static final RowConstraints ROW_CONSTRAINTS_FULL_LIST =
-            ROW_CONSTRAINTS_SIMPLE.newBuilder().setToggleAllowed(true).build();
+            new RowConstraints.Builder(ROW_CONSTRAINTS_SIMPLE).setToggleAllowed(true).build();
 
     private final int mMaxTextLinesPerRow;
     private final int mMaxActionsExclusive;
@@ -85,14 +87,6 @@
         return new Builder();
     }
 
-    /**
-     * Returns a new builder that contains the same data as this {@link RowConstraints} instance.
-     */
-    @NonNull
-    public Builder newBuilder() {
-        return new Builder(this);
-    }
-
     /** Returns whether the row can have a click listener associated with it. */
     public boolean isOnClickListenerAllowed() {
         return mIsOnClickListenerAllowed;
@@ -225,10 +219,17 @@
         }
 
         /** Returns an empty {@link Builder} instance. */
-        Builder() {
+        public Builder() {
         }
 
-        Builder(RowConstraints constraints) {
+        /**
+         * Returns a new builder that contains the same data as the given {@link RowConstraints}
+         * instance.
+         *
+         * @throws NullPointerException if {@code latLng} is {@code null}.
+         */
+        public Builder(@NonNull RowConstraints constraints) {
+            requireNonNull(constraints);
             mIsOnClickListenerAllowed = constraints.isOnClickListenerAllowed();
             mMaxTextLines = constraints.getMaxTextLinesPerRow();
             mMaxActionsExclusive = constraints.getMaxActionsExclusive();
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
index d0b1464..e4bbcf5 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
@@ -21,6 +21,8 @@
 import static androidx.car.app.model.constraints.RowConstraints.ROW_CONSTRAINTS_PANE;
 import static androidx.car.app.model.constraints.RowConstraints.ROW_CONSTRAINTS_SIMPLE;
 
+import static java.util.Objects.requireNonNull;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.car.app.model.Action;
@@ -50,8 +52,7 @@
     /** Default constraints for heterogeneous pane of items, full width. */
     @NonNull
     public static final RowListConstraints ROW_LIST_CONSTRAINTS_PANE =
-            ROW_LIST_CONSTRAINTS_CONSERVATIVE
-                    .newBuilder()
+            new RowListConstraints.Builder(ROW_LIST_CONSTRAINTS_CONSERVATIVE)
                     .setMaxActions(2)
                     .setRowConstraints(ROW_CONSTRAINTS_PANE)
                     .setAllowSelectableLists(false)
@@ -60,16 +61,14 @@
     /** Default constraints for uniform lists of items, no toggles. */
     @NonNull
     public static final RowListConstraints ROW_LIST_CONSTRAINTS_SIMPLE =
-            ROW_LIST_CONSTRAINTS_CONSERVATIVE
-                    .newBuilder()
+            new RowListConstraints.Builder(ROW_LIST_CONSTRAINTS_CONSERVATIVE)
                     .setRowConstraints(ROW_CONSTRAINTS_SIMPLE)
                     .build();
 
     /** Default constraints for the route preview card. */
     @NonNull
     public static final RowListConstraints ROW_LIST_CONSTRAINTS_ROUTE_PREVIEW =
-            ROW_LIST_CONSTRAINTS_CONSERVATIVE
-                    .newBuilder()
+            new RowListConstraints.Builder(ROW_LIST_CONSTRAINTS_CONSERVATIVE)
                     .setRowConstraints(ROW_CONSTRAINTS_SIMPLE)
                     .setAllowSelectableLists(true)
                     .build();
@@ -77,8 +76,7 @@
     /** Default constraints for uniform lists of items, full width (simple + toggle support). */
     @NonNull
     public static final RowListConstraints ROW_LIST_CONSTRAINTS_FULL_LIST =
-            ROW_LIST_CONSTRAINTS_CONSERVATIVE
-                    .newBuilder()
+            new RowListConstraints.Builder(ROW_LIST_CONSTRAINTS_CONSERVATIVE)
                     .setRowConstraints(ROW_CONSTRAINTS_FULL_LIST)
                     .setAllowSelectableLists(true)
                     .build();
@@ -94,12 +92,6 @@
         return new Builder();
     }
 
-    /** Return a a new builder for this {@link RowListConstraints} instance. */
-    @NonNull
-    public Builder newBuilder() {
-        return new Builder(this);
-    }
-
     /** Returns the maximum number of actions allowed to be added alongside the list. */
     public int getMaxActions() {
         return mMaxActions;
@@ -220,7 +212,13 @@
         public Builder() {
         }
 
-        Builder(RowListConstraints constraints) {
+        /**
+         * Return a a new builder for the given {@link RowListConstraints} instance.
+         *
+         * @throws NullPointerException if {@code latLng} is {@code null}.
+         */
+        public Builder(@NonNull RowListConstraints constraints) {
+            requireNonNull(constraints);
             this.mMaxActions = constraints.getMaxActions();
             this.mRowConstraints = constraints.getRowConstraints();
             this.mAllowSelectableLists = constraints.isAllowSelectableLists();
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java
index 867f254..7d9b95c 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java
@@ -70,15 +70,6 @@
         return new Builder(requireNonNull(cue));
     }
 
-    /**
-     * Returns a new {@link Builder} instance configured with the same data as this {@link Step}
-     * instance.
-     */
-    @NonNull
-    public Builder newBuilder() {
-        return new Builder(this);
-    }
-
     @Nullable
     public Maneuver getManeuver() {
         return mManeuver;
@@ -193,19 +184,9 @@
             this.mCue = CarText.create(requireNonNull(cue));
         }
 
-        Builder(Step step) {
-            this.mManeuver = step.getManeuver();
-            this.mLanes.clear();
-            this.mLanes.addAll(step.getLanes());
-            this.mLanesImage = step.getLanesImage();
-            this.mCue = requireNonNull(step.getCue());
-            this.mRoad = step.getRoad();
-        }
-
         /**
          * Sets the maneuver to be performed on this step or {@code null} if this step doesn't
-         * involve a
-         * maneuver.
+         * involve a maneuver.
          */
         @NonNull
         public Builder setManeuver(@Nullable Maneuver maneuver) {
diff --git a/car/app/app/src/test/java/androidx/car/app/model/ActionTest.java b/car/app/app/src/test/java/androidx/car/app/model/ActionTest.java
index 414e404..8232f95 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/ActionTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ActionTest.java
@@ -135,34 +135,6 @@
     }
 
     @Test
-    public void create_invalidSetOnBackThrows() {
-        assertThrows(
-                IllegalStateException.class,
-                () -> Action.BACK.newBuilder().setOnClickListener(() -> {
-                }).build());
-        assertThrows(
-                IllegalStateException.class,
-                () -> Action.BACK.newBuilder().setTitle("BACK").build());
-        assertThrows(
-                IllegalStateException.class,
-                () -> Action.BACK.newBuilder().setIcon(CarIcon.ALERT).build());
-    }
-
-    @Test
-    public void create_invalidSetOnAppIconThrows() {
-        assertThrows(
-                IllegalStateException.class,
-                () -> Action.APP_ICON.newBuilder().setOnClickListener(() -> {
-                }).build());
-        assertThrows(
-                IllegalStateException.class,
-                () -> Action.APP_ICON.newBuilder().setTitle("APP").build());
-        assertThrows(
-                IllegalStateException.class,
-                () -> Action.APP_ICON.newBuilder().setIcon(CarIcon.ALERT).build());
-    }
-
-    @Test
     public void equals() {
         String title = "foo";
         CarIcon icon = CarIcon.ALERT;
diff --git a/car/app/app/src/test/java/androidx/car/app/model/CarIconTest.java b/car/app/app/src/test/java/androidx/car/app/model/CarIconTest.java
index 79f15fb..ef80907 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/CarIconTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/CarIconTest.java
@@ -77,7 +77,7 @@
 
     @Test
     public void newBuilder_fromStandard() {
-        CarIcon carIcon = BACK.newBuilder().setTint(GREEN).build();
+        CarIcon carIcon = new CarIcon.Builder(BACK).setTint(GREEN).build();
 
         assertThat(carIcon.getType()).isEqualTo(TYPE_BACK);
         assertThat(carIcon.getTint()).isEqualTo(GREEN);
@@ -144,6 +144,6 @@
 
     @Test
     public void notEquals() {
-        assertThat(BACK.newBuilder().setTint(GREEN).build()).isNotEqualTo(BACK);
+        assertThat(new CarIcon.Builder(BACK).setTint(GREEN).build()).isNotEqualTo(BACK);
     }
 }
diff --git a/car/app/app/src/test/java/androidx/car/app/model/MessageTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/model/MessageTemplateTest.java
index 9909aec..8000014 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/MessageTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/MessageTemplateTest.java
@@ -111,7 +111,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setHeaderAction(Action.BACK)
-                        .setDebugCause(exception)
+                        .setDebugMessage(exception)
                         .setIcon(icon)
                         .setActionList(ImmutableList.of(action))
                         .build();
@@ -131,7 +131,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setHeaderAction(Action.BACK)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
@@ -140,7 +140,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setHeaderAction(Action.BACK)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
@@ -155,7 +155,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
                         .build();
@@ -163,7 +163,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage("yo")
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
                         .build();
@@ -177,7 +177,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
                         .build();
@@ -185,7 +185,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(new IllegalStateException("something else bad"))
+                        .setDebugMessage(new IllegalStateException("something else bad"))
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
                         .build();
@@ -199,8 +199,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
-                        .setMessage(mMessage)
+                        .setDebugMessage(mCause)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
                         .build();
@@ -208,7 +207,7 @@
                 new MessageTemplate.Builder("bar")
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
                         .build();
@@ -222,7 +221,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setHeaderAction(Action.BACK)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
@@ -231,7 +230,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setHeaderAction(Action.APP_ICON)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
@@ -246,7 +245,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
                         .build();
@@ -254,7 +253,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setActionList(ImmutableList.of(mAction, mAction))
                         .setIcon(mIcon)
                         .build();
@@ -268,7 +267,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
                         .build();
@@ -276,7 +275,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(CarIcon.ERROR)
                         .build();
@@ -290,7 +289,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle(mTitle)
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
                         .build();
@@ -298,7 +297,7 @@
                 new MessageTemplate.Builder(mMessage)
                         .setTitle("Header2")
                         .setDebugMessage(mDebugMessage)
-                        .setDebugCause(mCause)
+                        .setDebugMessage(mCause)
                         .setActionList(ImmutableList.of(mAction))
                         .setIcon(mIcon)
                         .build();
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index 3ecaca6..df76239 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -9,6 +9,7 @@
     ctor public Animatable(T? initialValue, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? visibilityThreshold);
     method public suspend Object? animateDecay(T? initialVelocity, androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Animatable<T,V>,kotlin.Unit>? block, optional kotlin.coroutines.Continuation<? super androidx.compose.animation.core.AnimationResult<T,V>> p);
     method public suspend Object? animateTo(T? targetValue, optional androidx.compose.animation.core.AnimationSpec<T> animationSpec, optional T? initialVelocity, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Animatable<T,V>,kotlin.Unit>? block, optional kotlin.coroutines.Continuation<? super androidx.compose.animation.core.AnimationResult<T,V>> p);
+    method public androidx.compose.runtime.State<T> asState();
     method public T? getLowerBound();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
diff --git a/compose/animation/animation-core/api/public_plus_experimental_current.txt b/compose/animation/animation-core/api/public_plus_experimental_current.txt
index 3ecaca6..df76239 100644
--- a/compose/animation/animation-core/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation-core/api/public_plus_experimental_current.txt
@@ -9,6 +9,7 @@
     ctor public Animatable(T? initialValue, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? visibilityThreshold);
     method public suspend Object? animateDecay(T? initialVelocity, androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Animatable<T,V>,kotlin.Unit>? block, optional kotlin.coroutines.Continuation<? super androidx.compose.animation.core.AnimationResult<T,V>> p);
     method public suspend Object? animateTo(T? targetValue, optional androidx.compose.animation.core.AnimationSpec<T> animationSpec, optional T? initialVelocity, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Animatable<T,V>,kotlin.Unit>? block, optional kotlin.coroutines.Continuation<? super androidx.compose.animation.core.AnimationResult<T,V>> p);
+    method public androidx.compose.runtime.State<T> asState();
     method public T? getLowerBound();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
diff --git a/compose/animation/animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
index c5a861a..6f1cc23b 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -9,6 +9,7 @@
     ctor public Animatable(T? initialValue, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? visibilityThreshold);
     method public suspend Object? animateDecay(T? initialVelocity, androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Animatable<T,V>,kotlin.Unit>? block, optional kotlin.coroutines.Continuation<? super androidx.compose.animation.core.AnimationResult<T,V>> p);
     method public suspend Object? animateTo(T? targetValue, optional androidx.compose.animation.core.AnimationSpec<T> animationSpec, optional T? initialVelocity, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Animatable<T,V>,kotlin.Unit>? block, optional kotlin.coroutines.Continuation<? super androidx.compose.animation.core.AnimationResult<T,V>> p);
+    method public androidx.compose.runtime.State<T> asState();
     method public T? getLowerBound();
     method public T! getTargetValue();
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
diff --git a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/InfiniteTransitionSamples.kt b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/InfiniteTransitionSamples.kt
index d01dc7c..1977ca9 100644
--- a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/InfiniteTransitionSamples.kt
+++ b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/InfiniteTransitionSamples.kt
@@ -83,7 +83,8 @@
         Box(Modifier.fillMaxSize()) {
             Icon(
                 Icons.Filled.Favorite,
-                Modifier.align(Alignment.Center)
+                contentDescription = null,
+                modifier = Modifier.align(Alignment.Center)
                     .graphicsLayer(
                         scaleX = scale,
                         scaleY = scale
diff --git a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/SuspendAnimationSamples.kt b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/SuspendAnimationSamples.kt
index 43d0a9e..01fb138 100644
--- a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/SuspendAnimationSamples.kt
+++ b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/SuspendAnimationSamples.kt
@@ -93,7 +93,8 @@
         Box(Modifier.fillMaxSize()) {
             Icon(
                 Icons.Filled.Favorite,
-                Modifier.align(Alignment.Center)
+                contentDescription = null,
+                modifier = Modifier.align(Alignment.Center)
                     .graphicsLayer(
                         scaleX = 3.0f,
                         scaleY = 3.0f,
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt
index 535d987..75f62a2 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt
@@ -20,6 +20,7 @@
 import androidx.compose.animation.core.AnimationEndReason.Finished
 import androidx.compose.animation.core.AnimationEndReason.Interrupted
 import androidx.compose.runtime.AtomicReference
+import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -381,6 +382,13 @@
         endAnimation()
     }
 
+    /**
+     * Returns a [State] representing the current [value] of this animation. This allows
+     * hoisting the animation's current value without causing unnecessary recompositions
+     * when the value changes.
+     */
+    fun asState(): State<T> = internalState
+
     private fun Job.cancelAnimation() {
         cancel(AnimationCancellationException())
     }
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteAnimationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteAnimationDemo.kt
index e0aa04d..689c1c3 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteAnimationDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteAnimationDemo.kt
@@ -52,7 +52,8 @@
     Box(Modifier.fillMaxSize()) {
         Icon(
             Icons.Filled.Favorite,
-            Modifier.align(Alignment.Center)
+            contentDescription = null,
+            modifier = Modifier.align(Alignment.Center)
                 .graphicsLayer(
                     scaleX = 3.0f,
                     scaleY = 3.0f,
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteTransitionDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteTransitionDemo.kt
index efc6be4..34e6bb1 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteTransitionDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/InfiniteTransitionDemo.kt
@@ -60,6 +60,7 @@
     Box(Modifier.fillMaxSize()) {
         Icon(
             Icons.Filled.Favorite,
+            null,
             Modifier.align(Alignment.Center)
                 .graphicsLayer(
                     scaleX = scale,
diff --git a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt
index 7ed33cb..959558b 100644
--- a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt
+++ b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt
@@ -170,7 +170,11 @@
         modifier = with(ColumnScope) { Modifier.align(Alignment.CenterHorizontally) }
     ) {
         Row(Modifier.padding(start = 12.dp, end = 12.dp)) {
-            Icon(Icons.Default.Favorite, Modifier.align(Alignment.CenterVertically))
+            Icon(
+                Icons.Default.Favorite,
+                contentDescription = "Favorite",
+                modifier = Modifier.align(Alignment.CenterVertically)
+            )
             AnimatedVisibility(
                 expanded,
                 modifier = Modifier.align(Alignment.CenterVertically)
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/example1/Main.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/example1/Main.kt
index 1bcfea1..64dcdec 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/example1/Main.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/example1/Main.kt
@@ -117,7 +117,7 @@
                 TopAppBar(
                     title = {
                         Row(verticalAlignment = Alignment.CenterVertically) {
-                            Icon(Icons.Outlined.Home)
+                            Icon(Icons.Outlined.Home, "Home")
                             Text(title)
                         }
                     }
@@ -137,7 +137,7 @@
                     IconButton(
                         >
                     ) {
-                        Icon(Icons.Filled.Menu, Modifier.size(ButtonDefaults.IconSize))
+                        Icon(Icons.Filled.Menu, "Menu", Modifier.size(ButtonDefaults.IconSize))
                     }
                 }
             },
@@ -397,11 +397,13 @@
         Row {
             Image(
                 imageResource("androidx/compose/desktop/example/circus.jpg"),
+                "Localized description",
                 Modifier.size(200.dp)
             )
 
             Icon(
                 vectorXmlResource("androidx/compose/desktop/example/ic_baseline_deck_24.xml"),
+                "Localized description",
                 Modifier.size(100.dp).align(Alignment.CenterVertically),
                 tint = Color.Blue.copy(alpha = 0.5f)
             )
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/ConstraintLayoutTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/ConstraintLayoutTest.kt
index 82c219c..ae7cf66 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/ConstraintLayoutTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/ConstraintLayoutTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.layout
 
+import android.content.Context
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.Modifier
@@ -34,6 +35,7 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
@@ -52,11 +54,17 @@
     @get:Rule
     val rule = createComposeRule()
 
+    var displaySize: IntSize = IntSize.Zero
+
     // region sizing tests
 
     @Before
     fun before() {
         isDebugInspectorInfoEnabled = true
+        displaySize = ApplicationProvider
+            .getApplicationContext<Context>().resources.displayMetrics.let {
+                IntSize(it.widthPixels, it.heightPixels)
+            }
     }
 
     @After
@@ -68,6 +76,7 @@
     fun dividerMatchTextHeight_spread() = with(density) {
         val aspectRatioBoxSize = Ref<IntSize>()
         val dividerSize = Ref<IntSize>()
+
         rule.setContent {
             ConstraintLayout(
                 // Make CL fixed width and wrap content height.
@@ -85,7 +94,7 @@
                             height = Dimension.wrapContent
                         }
                         // Try to be large to make wrap content impossible.
-                        .preferredWidth((rule.displaySize.width).toDp())
+                        .preferredWidth((displaySize.width).toDp())
                         // This could be any (width in height out child) e.g. text
                         .aspectRatio(2f)
                         .onGloballyPositioned { coordinates ->
@@ -108,12 +117,12 @@
         rule.runOnIdle {
             // The aspect ratio could not wrap and it is wrap suggested, so it respects constraints.
             assertEquals(
-                (rule.displaySize.width / 2),
+                (displaySize.width / 2),
                 aspectRatioBoxSize.value!!.width
             )
             // Aspect ratio is preserved.
             assertEquals(
-                (rule.displaySize.width / 2 / 2),
+                (displaySize.width / 2 / 2),
                 aspectRatioBoxSize.value!!.height
             )
             // Divider has fixed width 1.dp in constraint set.
@@ -145,7 +154,7 @@
                             height = Dimension.preferredWrapContent
                         }
                         // Try to be large to make wrap content impossible.
-                        .preferredWidth((rule.displaySize.width).toDp())
+                        .preferredWidth((displaySize.width).toDp())
                         // This could be any (width in height out child) e.g. text
                         .aspectRatio(2f)
                         .onGloballyPositioned { coordinates ->
@@ -168,12 +177,12 @@
         rule.runOnIdle {
             // The aspect ratio could not wrap and it is wrap suggested, so it respects constraints.
             assertEquals(
-                (rule.displaySize.width / 2),
+                (displaySize.width / 2),
                 aspectRatioBoxSize.value!!.width
             )
             // Aspect ratio is preserved.
             assertEquals(
-                (rule.displaySize.width / 2 / 2),
+                (displaySize.width / 2 / 2),
                 aspectRatioBoxSize.value!!.height
             )
             // Divider has fixed width 1.dp in constraint set.
@@ -205,7 +214,7 @@
                             height = Dimension.wrapContent
                         }
                         // Try to be large to make wrap content impossible.
-                        .preferredWidth((rule.displaySize.width).toDp())
+                        .preferredWidth((displaySize.width).toDp())
                         // This could be any (width in height out child) e.g. text
                         .aspectRatio(2f)
                         .onGloballyPositioned { coordinates ->
@@ -229,12 +238,12 @@
         rule.runOnIdle {
             // The aspect ratio could not wrap and it is wrap suggested, so it respects constraints.
             assertEquals(
-                (rule.displaySize.width / 2),
+                (displaySize.width / 2),
                 aspectRatioBoxSize.value!!.width
             )
             // Aspect ratio is preserved.
             assertEquals(
-                (rule.displaySize.width / 2 / 2),
+                (displaySize.width / 2 / 2),
                 aspectRatioBoxSize.value!!.height
             )
             // Divider has fixed width 1.dp in constraint set.
@@ -267,7 +276,7 @@
                             height = Dimension.wrapContent
                         }
                         // Try to be large to make wrap content impossible.
-                        .preferredWidth((rule.displaySize.width).toDp())
+                        .preferredWidth((displaySize.width).toDp())
                         // This could be any (width in height out child) e.g. text
                         .aspectRatio(2f)
                         .onGloballyPositioned { coordinates ->
@@ -291,12 +300,12 @@
         rule.runOnIdle {
             // The aspect ratio could not wrap and it is wrap suggested, so it respects constraints.
             assertEquals(
-                (rule.displaySize.width / 2),
+                (displaySize.width / 2),
                 aspectRatioBoxSize.value!!.width
             )
             // Aspect ratio is preserved.
             assertEquals(
-                (rule.displaySize.width / 2 / 2),
+                (displaySize.width / 2 / 2),
                 aspectRatioBoxSize.value!!.height
             )
             // Divider has fixed width 1.dp in constraint set.
@@ -423,8 +432,8 @@
             }
         }
 
-        val displayWidth = rule.displaySize.width
-        val displayHeight = rule.displaySize.height
+        val displayWidth = displaySize.width
+        val displayHeight = displaySize.height
 
         rule.runOnIdle {
             assertEquals(
@@ -493,8 +502,8 @@
             }
         }
 
-        val displayWidth = rule.displaySize.width
-        val displayHeight = rule.displaySize.height
+        val displayWidth = displaySize.width
+        val displayHeight = displaySize.height
 
         rule.runOnIdle {
             assertEquals(
@@ -569,8 +578,8 @@
             }
         }
 
-        val displayWidth = rule.displaySize.width
-        val displayHeight = rule.displaySize.height
+        val displayWidth = displaySize.width
+        val displayHeight = displaySize.height
 
         rule.runOnIdle {
             assertEquals(
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt
index 7a1d75e..ba7afda 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Arrangement.kt
@@ -45,11 +45,12 @@
         /**
          * Horizontally places the layout children.
          *
-         * @param totalSize Available space that can be occupied by the children.
-         * @param sizes An array of sizes of all children.
+         * @param totalSize Available space that can be occupied by the children, in pixels.
+         * @param sizes An array of sizes of all children, in pixels.
          * @param layoutDirection A layout direction, left-to-right or right-to-left, of the parent
          * layout that should be taken into account when determining positions of the children.
-         * @param outPositions An array of the size of [sizes] that returns the calculated positions.
+         * @param outPositions An array of the size of [sizes] that returns the calculated
+         * positions relative to the left, in pixels.
          */
         fun Density.arrange(
             totalSize: Int,
@@ -72,9 +73,10 @@
         /**
          * Vertically places the layout children.
          *
-         * @param totalSize Available space that can be occupied by the children.
-         * @param sizes An array of sizes of all children.
-         * @param outPositions An array of the size of [sizes] that returns the calculated positions.
+         * @param totalSize Available space that can be occupied by the children, in pixels.
+         * @param sizes An array of sizes of all children, in pixels.
+         * @param outPositions An array of the size of [sizes] that returns the calculated
+         * positions relative to the top, in pixels.
          */
         fun Density.arrange(
             totalSize: Int,
@@ -98,7 +100,8 @@
 
     /**
      * Place children horizontally such that they are as close as possible to the beginning of the
-     * main axis.
+     * horizontal axis (left if the layout direction is LTR, right otherwise).
+     * Visually: 123#### for LTR and ####321.
      */
     @Stable
     val Start = object : Horizontal {
@@ -119,6 +122,7 @@
     /**
      * Place children horizontally such that they are as close as possible to the end of the main
      * axis.
+     * Visually: ####123 for LTR and 321#### for RTL.
      */
     @Stable
     val End = object : Horizontal {
@@ -139,6 +143,7 @@
     /**
      * Place children vertically such that they are as close as possible to the top of the main
      * axis.
+     * Visually: (top) 123#### (bottom)
      */
     @Stable
     val Top = object : Vertical {
@@ -152,6 +157,7 @@
     /**
      * Place children vertically such that they are as close as possible to the bottom of the main
      * axis.
+     * Visually: (top) ####123 (bottom)
      */
     @Stable
     val Bottom = object : Vertical {
@@ -164,6 +170,7 @@
 
     /**
      * Place children such that they are as close as possible to the middle of the main axis.
+     * Visually: ##123## for LTR and ##321## for RTL.
      */
     @Stable
     val Center = object : HorizontalOrVertical {
@@ -192,6 +199,7 @@
     /**
      * Place children such that they are spaced evenly across the main axis, including free
      * space before the first child and after the last child.
+     * Visually: #1#2#3# for LTR and #3#2#1# for RTL.
      */
     @Stable
     val SpaceEvenly = object : HorizontalOrVertical {
@@ -220,6 +228,7 @@
     /**
      * Place children such that they are spaced evenly across the main axis, without free
      * space before the first child or after the last child.
+     * Visually: 1##2##3 for LTR or 3##2##1 for RTL.
      */
     @Stable
     val SpaceBetween = object : HorizontalOrVertical {
@@ -249,6 +258,7 @@
      * Place children such that they are spaced evenly across the main axis, including free
      * space before the first child and after the last child, but half the amount of space
      * existing otherwise between two consecutive children.
+     * Visually: #1##2##3# for LTR and #3##2##1# for RTL
      */
     @Stable
     val SpaceAround = object : HorizontalOrVertical {
@@ -345,6 +355,8 @@
          *
          * Unlike [Arrangement.Start], when the layout direction is RTL, the children will not be
          * mirrored and as such children will appear in the order they are composed inside the [Row].
+         *
+         * Visually: 123####
          */
         @Stable
         val Left = object : Horizontal {
@@ -361,6 +373,8 @@
          *
          * Unlike [Arrangement.Center], when the layout direction is RTL, the children will not be
          * mirrored and as such children will appear in the order they are composed inside the [Row].
+         *
+         * Visually: ##123##
          */
         @Stable
         val Center = object : Horizontal {
@@ -378,6 +392,8 @@
          *
          * Unlike [Arrangement.End], when the layout direction is RTL, the children will not be
          * mirrored and as such children will appear in the order they are composed inside the [Row].
+         *
+         * Visually: ####123
          */
         @Stable
         val Right = object : Horizontal {
@@ -395,6 +411,8 @@
          *
          * Unlike [Arrangement.SpaceBetween], when the layout direction is RTL, the children will not be
          * mirrored and as such children will appear in the order they are composed inside the [Row].
+         *
+         * Visually: 1##2##3
          */
         @Stable
         val SpaceBetween = object : Horizontal {
@@ -412,6 +430,8 @@
          *
          * Unlike [Arrangement.SpaceEvenly], when the layout direction is RTL, the children will not be
          * mirrored and as such children will appear in the order they are composed inside the [Row].
+         *
+         * Visually: #1#2#3#
          */
         @Stable
         val SpaceEvenly = object : Horizontal {
@@ -430,6 +450,8 @@
          *
          * Unlike [Arrangement.SpaceAround], when the layout direction is RTL, the children will not be
          * mirrored and as such children will appear in the order they are composed inside the [Row].
+         *
+         * Visually: #1##2##3##4#
          */
         @Stable
         val SpaceAround = object : Horizontal {
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt
index 78f6cec..7a161de 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt
@@ -40,7 +40,7 @@
  * Padding is applied before content measurement and takes precedence; content may only be as large
  * as the remaining space.
  *
- * Negative padding is not permitted. See [offset].
+ * Negative padding is not permitted. See [Modifier.offset].
  *
  * Example usage:
  * @sample androidx.compose.foundation.layout.samples.PaddingModifier
@@ -74,7 +74,7 @@
  * Padding is applied before content measurement and takes precedence; content may only be as large
  * as the remaining space.
  *
- * Negative padding is not permitted. See [offset].
+ * Negative padding is not permitted. See [Modifier.offset].
  *
  * Example usage:
  * @sample androidx.compose.foundation.layout.samples.SymmetricPaddingModifier
@@ -103,7 +103,7 @@
  * Padding is applied before content measurement and takes precedence; content may only be as large
  * as the remaining space.
  *
- * Negative padding is not permitted. See [offset].
+ * Negative padding is not permitted. See [Modifier.offset].
  *
  * Example usage:
  * @sample androidx.compose.foundation.layout.samples.PaddingAllModifier
@@ -129,7 +129,7 @@
  * top, right and bottom. Padding is applied before content measurement and takes precedence;
  * content may only be as large as the remaining space.
  *
- * Negative padding is not permitted. See [offset].
+ * Negative padding is not permitted. See [Modifier.offset].
  *
  * Example usage:
  * @sample androidx.compose.foundation.layout.samples.PaddingValuesModifier
@@ -158,7 +158,7 @@
  * [padding] to apply relative paddings. Padding is applied before content measurement and takes
  * precedence; content may only be as large as the remaining space.
  *
- * Negative padding is not permitted. See [offset].
+ * Negative padding is not permitted. See [Modifier.offset].
  *
  * Example usage:
  * @sample androidx.compose.foundation.layout.samples.AbsolutePaddingModifier
@@ -258,5 +258,14 @@
     @Stable
     val bottom: Dp = 0.dp
 ) {
+    /**
+     * Describes a padding of [all] dp along all 4 edges.
+     */
     constructor(all: Dp) : this(all, all, all, all)
+
+    /**
+     * Describes a padding of [horizontal] dp along the left and right edges, and of [vertical]
+     * dp along the top and bottom edges.
+     */
+    constructor(horizontal: Dp, vertical: Dp) : this(horizontal, vertical, horizontal, vertical)
 }
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index a4ee665..e2ea5c1 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -66,9 +66,9 @@
   }
 
   public final class ImageKt {
-    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.ImageVector imageVector, 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);
+    method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, 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.ImageVector imageVector, String? contentDescription, 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, String? contentDescription, 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);
   }
 
   @androidx.compose.runtime.Stable public interface Indication {
@@ -510,8 +510,8 @@
   }
 
   public final class BasicTextFieldKt {
-    method @androidx.compose.runtime.Composable public static void BasicTextField-55_Anxs(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor);
-    method @androidx.compose.runtime.Composable public static void BasicTextField-HcQ_yMQ(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor);
+    method @androidx.compose.runtime.Composable public static void BasicTextField-Q80SffI(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
+    method @androidx.compose.runtime.Composable public static void BasicTextField-qDYpSg4(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
   }
 
   public final class BasicTextKt {
@@ -520,7 +520,7 @@
   }
 
   public final class CoreTextFieldKt {
-    method @androidx.compose.runtime.Composable @androidx.compose.ui.text.InternalTextApi public static void CoreTextField-j_8B-p4(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState? interactionState, optional long cursorColor, optional boolean softWrap, optional androidx.compose.ui.text.input.ImeOptions imeOptions);
+    method @androidx.compose.runtime.Composable @androidx.compose.ui.text.InternalTextApi public static void CoreTextField-KzZcjnU(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState? interactionState, optional long cursorColor, optional boolean softWrap, optional int maxLines, optional androidx.compose.ui.text.input.ImeOptions imeOptions, optional boolean enabled, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
   }
 
   public final class CoreTextKt {
@@ -574,12 +574,18 @@
   public final class TextFieldDragGestureFilterKt {
   }
 
+  public final class TextFieldGestureModifiersKt {
+  }
+
   public final class TextFieldScrollKt {
   }
 
   public final class TextFieldSizeKt {
   }
 
+  public final class TextLayoutResultProxyKt {
+  }
+
 }
 
 package androidx.compose.foundation.text.selection {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index a4ee665..e2ea5c1 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -66,9 +66,9 @@
   }
 
   public final class ImageKt {
-    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.ImageVector imageVector, 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);
+    method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, 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.ImageVector imageVector, String? contentDescription, 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, String? contentDescription, 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);
   }
 
   @androidx.compose.runtime.Stable public interface Indication {
@@ -510,8 +510,8 @@
   }
 
   public final class BasicTextFieldKt {
-    method @androidx.compose.runtime.Composable public static void BasicTextField-55_Anxs(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor);
-    method @androidx.compose.runtime.Composable public static void BasicTextField-HcQ_yMQ(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor);
+    method @androidx.compose.runtime.Composable public static void BasicTextField-Q80SffI(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
+    method @androidx.compose.runtime.Composable public static void BasicTextField-qDYpSg4(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
   }
 
   public final class BasicTextKt {
@@ -520,7 +520,7 @@
   }
 
   public final class CoreTextFieldKt {
-    method @androidx.compose.runtime.Composable @androidx.compose.ui.text.InternalTextApi public static void CoreTextField-j_8B-p4(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState? interactionState, optional long cursorColor, optional boolean softWrap, optional androidx.compose.ui.text.input.ImeOptions imeOptions);
+    method @androidx.compose.runtime.Composable @androidx.compose.ui.text.InternalTextApi public static void CoreTextField-KzZcjnU(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState? interactionState, optional long cursorColor, optional boolean softWrap, optional int maxLines, optional androidx.compose.ui.text.input.ImeOptions imeOptions, optional boolean enabled, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
   }
 
   public final class CoreTextKt {
@@ -574,12 +574,18 @@
   public final class TextFieldDragGestureFilterKt {
   }
 
+  public final class TextFieldGestureModifiersKt {
+  }
+
   public final class TextFieldScrollKt {
   }
 
   public final class TextFieldSizeKt {
   }
 
+  public final class TextLayoutResultProxyKt {
+  }
+
 }
 
 package androidx.compose.foundation.text.selection {
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index a4ee665..e2ea5c1 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -66,9 +66,9 @@
   }
 
   public final class ImageKt {
-    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.ImageVector imageVector, 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);
+    method @androidx.compose.runtime.Composable public static inline void Image(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, 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.ImageVector imageVector, String? contentDescription, 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, String? contentDescription, 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);
   }
 
   @androidx.compose.runtime.Stable public interface Indication {
@@ -510,8 +510,8 @@
   }
 
   public final class BasicTextFieldKt {
-    method @androidx.compose.runtime.Composable public static void BasicTextField-55_Anxs(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor);
-    method @androidx.compose.runtime.Composable public static void BasicTextField-HcQ_yMQ(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor);
+    method @androidx.compose.runtime.Composable public static void BasicTextField-Q80SffI(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
+    method @androidx.compose.runtime.Composable public static void BasicTextField-qDYpSg4(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional boolean singleLine, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState interactionState, optional long cursorColor, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
   }
 
   public final class BasicTextKt {
@@ -520,7 +520,7 @@
   }
 
   public final class CoreTextFieldKt {
-    method @androidx.compose.runtime.Composable @androidx.compose.ui.text.InternalTextApi public static void CoreTextField-j_8B-p4(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState? interactionState, optional long cursorColor, optional boolean softWrap, optional androidx.compose.ui.text.input.ImeOptions imeOptions);
+    method @androidx.compose.runtime.Composable @androidx.compose.ui.text.InternalTextApi public static void CoreTextField-KzZcjnU(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional androidx.compose.foundation.InteractionState? interactionState, optional long cursorColor, optional boolean softWrap, optional int maxLines, optional androidx.compose.ui.text.input.ImeOptions imeOptions, optional boolean enabled, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
   }
 
   public final class CoreTextKt {
@@ -574,12 +574,18 @@
   public final class TextFieldDragGestureFilterKt {
   }
 
+  public final class TextFieldGestureModifiersKt {
+  }
+
   public final class TextFieldScrollKt {
   }
 
   public final class TextFieldSizeKt {
   }
 
+  public final class TextLayoutResultProxyKt {
+  }
+
 }
 
 package androidx.compose.foundation.text.selection {
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt
index 5aaa22ea..d1cc274 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt
@@ -18,14 +18,26 @@
 
 import androidx.annotation.Sampled
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.Text
 import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material.Icon
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.MailOutline
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.savedinstancestate.savedInstanceState
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.dp
 
 @Sampled
 @Composable
@@ -69,4 +81,30 @@
             Text(text = "Placeholder")
         }
     }
+}
+
+@Sampled
+@Composable
+@OptIn(ExperimentalFoundationApi::class)
+fun TextFieldWithIconSample() {
+    var value by savedInstanceState { "initial value" }
+    BasicTextField(
+        value = value,
+         value = it },
+        decorationBox = { innerTextField ->
+            // Because the decorationBox is used, the whole Row gets the same behaviour as the
+            // internal input field would have otherwise. For example, there is no need to add a
+            // Modifier.clickable to the Row anymore to bring the text field into focus when user
+            // taps on a larger text field area which includes paddings and the icon areas.
+            Row(
+                Modifier
+                    .background(Color.LightGray, RoundedCornerShape(percent = 30))
+                    .padding(16.dp)
+            ) {
+                Icon(Icons.Default.MailOutline, contentDescription = null)
+                Spacer(Modifier.width(16.dp))
+                innerTextField()
+            }
+        }
+    )
 }
\ No newline at end of file
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 904f536..fd4f49f 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
@@ -43,7 +43,7 @@
 fun ImageSample() {
     val ImageBitmap = createTestImage()
     // Lays out and draws an image sized to the dimensions of the ImageBitmap
-    Image(bitmap = ImageBitmap)
+    Image(bitmap = ImageBitmap, contentDescription = "Localized description")
 }
 
 @Sampled
@@ -56,7 +56,8 @@
             ImageBitmap,
             IntOffset(10, 12),
             IntSize(50, 60)
-        )
+        ),
+        contentDescription = "Localized description"
     )
 }
 
@@ -67,6 +68,7 @@
     imageVector.resource.resource?.let {
         Image(
             imageVector = it,
+            contentDescription = null,
             modifier = Modifier.preferredSize(200.dp, 200.dp),
             contentScale = ContentScale.Fit,
             colorFilter = ColorFilter.tint(Color.Cyan)
@@ -89,7 +91,11 @@
         }
     }
 
-    Image(painter = customPainter, modifier = Modifier.preferredSize(100.dp, 100.dp))
+    Image(
+        painter = customPainter,
+        contentDescription = "Localized description",
+        modifier = Modifier.preferredSize(100.dp, 100.dp)
+    )
 }
 
 /**
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 b49f36d..1e6f611 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
@@ -56,6 +56,7 @@
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.res.imageResource
 import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.test.performClick
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
@@ -113,7 +114,11 @@
                     .background(color = Color.White)
                     .wrapContentSize(Alignment.Center)
             ) {
-                Image(modifier = Modifier.testTag(contentTag), bitmap = createImageBitmap())
+                Image(
+                    modifier = Modifier.testTag(contentTag),
+                    contentDescription = null,
+                    bitmap = createImageBitmap()
+                )
             }
         }
 
@@ -162,7 +167,8 @@
                             imageHeight / 2 - subsectionHeight / 2
                         ),
                         IntSize(subsectionWidth, subsectionHeight)
-                    )
+                    ),
+                    null
                 )
             }
         }
@@ -258,6 +264,7 @@
                 // the bounds
                 Image(
                     bitmap = createImageBitmap(),
+                    contentDescription = null,
                     modifier = Modifier
                         .testTag(contentTag)
                         .preferredSize(
@@ -325,6 +332,7 @@
             ) {
                 Image(
                     bitmap = ImageBitmap,
+                    contentDescription = null,
                     modifier = Modifier
                         .testTag(contentTag)
                         .preferredSize(
@@ -357,6 +365,7 @@
                 // ImageBitmap that is to be drawn in the bottom end section of the composable
                 Image(
                     bitmap = createImageBitmap(),
+                    contentDescription = null,
                     modifier = Modifier
                         .testTag(contentTag)
                         .preferredSize(
@@ -420,6 +429,7 @@
                 loadVectorResource(R.drawable.ic_vector_asset_test).resource.resource?.let {
                     Image(
                         it,
+                        null,
                         modifier = Modifier.preferredSizeIn(
                             minWidth = minWidth,
                             minHeight = minHeight
@@ -505,6 +515,7 @@
             val heightDp = asset.height / AmbientDensity.current.density
             Image(
                 asset,
+                null,
                 modifier = Modifier
                     .testTag(testTag)
                     .background(Color.Green)
@@ -540,14 +551,15 @@
             }
             Image(
                 painterResource(painterId.value),
-                contentScale = ContentScale.FillBounds,
+                null,
                 modifier = Modifier.testTag(testTag).clickable {
                     if (painterId.value == R.drawable.ic_vector_square_asset_test) {
                         painterId.value = R.drawable.ic_image_test
                     } else {
                         painterId.value = R.drawable.ic_vector_square_asset_test
                     }
-                }
+                },
+                contentScale = ContentScale.FillBounds
             )
         }
 
@@ -559,4 +571,20 @@
 
         rule.onNodeWithTag(testTag).captureToImage().assertPixels { imageColor }
     }
+
+    @Test
+    fun testImageContentDescription() {
+        val testTag = "TestTag"
+        rule.setContent {
+            Image(
+                bitmap = ImageBitmap(100, 100),
+                modifier = Modifier.testTag(testTag),
+                contentDescription = "asdf"
+            )
+        }
+        rule.onNodeWithTag(testTag).fetchSemanticsNode().let {
+            Assert.assertTrue(it.config.contains(SemanticsProperties.ContentDescription))
+            Assert.assertEquals(it.config[SemanticsProperties.ContentDescription], "asdf")
+        }
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldScrollTest.kt
index 06041a9..32474cd 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldScrollTest.kt
@@ -28,11 +28,14 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.preferredWidth
-import androidx.compose.foundation.text.CoreTextField
+import androidx.compose.foundation.text.BasicText
 import androidx.compose.foundation.text.TextFieldScrollerPosition
+import androidx.compose.foundation.text.TextLayoutResultProxy
 import androidx.compose.foundation.text.maxLinesHeight
 import androidx.compose.foundation.text.textFieldScroll
 import androidx.compose.foundation.verticalScroll
+import androidx.compose.foundation.text.textFieldScrollable
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
 import androidx.compose.testutils.assertPixels
@@ -45,6 +48,7 @@
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.ComposeTestRule
 import androidx.compose.ui.test.junit4.StateRestorationTester
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -61,7 +65,6 @@
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
@@ -73,6 +76,19 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+/**
+ * These tests are for testing the text field scrolling modifiers [Modifier.textFieldScroll] and
+ * [Modifier.textFieldScrollable] working together.
+ * The tests are structured in a way that
+ * - two modifiers are applied to the text which exposes its [TextLayoutResult]
+ * - swipe gesture applied
+ * - [TextFieldScrollerPosition] state is checked to see if scrolling happened
+ * Previously we were able to test using CoreTextField. But with the decoration box change these
+ * two modifiers are already applied to the CoreTextField internally. Therefore we have no access
+ * to the [TextFieldScrollerPosition] object anymore. As such, CoreTextField was replaced with
+ * [BasicText] which is equivalent for testing these modifiers
+ */
+
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalFoundationApi::class, InternalTextApi::class)
@@ -100,30 +116,12 @@
     }
 
     @Test
-    fun testTextField_horizontallyScrollable_withLongInput() {
-        val scrollerPosition = TextFieldScrollerPosition()
-        val value = TextFieldValue(longText)
+    fun textFieldScroll_horizontal_scrollable_withLongInput() {
+        val scrollerPosition = TextFieldScrollerPosition(Orientation.Horizontal)
 
-        rule.setContent {
-            val textLayoutResultRef: Ref<TextLayoutResult?> = remember { Ref() }
-            CoreTextField(
-                value = value,
-                >
-                 textLayoutResultRef.value = it },
-                softWrap = false,
-                modifier = Modifier
-                    .preferredSize(width = 300.dp, height = 50.dp)
-                    .maxLinesHeight(1, TextStyle.Default)
-                    .textFieldScroll(
-                        orientation = Orientation.Horizontal,
-                        remember { scrollerPosition },
-                        value,
-                        VisualTransformation.None,
-                        remember { InteractionState() },
-                        textLayoutResultRef
-                    )
-            )
-        }
+        rule.setupHorizontallyScrollableContent(
+            scrollerPosition, longText, Modifier.preferredSize(width = 300.dp, height = 50.dp)
+        )
 
         rule.runOnIdle {
             assertThat(scrollerPosition.maximum).isLessThan(Float.POSITIVE_INFINITY)
@@ -132,29 +130,14 @@
     }
 
     @Test
-    fun testTextField_verticallyScrollable_withLongInput() {
+    fun textFieldScroll_vertical_scrollable_withLongInput() {
         val scrollerPosition = TextFieldScrollerPosition()
-        val value = TextFieldValue(longText)
 
-        rule.setContent {
-            val textLayoutResultRef: Ref<TextLayoutResult?> = remember { Ref() }
-            CoreTextField(
-                value = value,
-                >
-                 textLayoutResultRef.value = it },
-                modifier = Modifier
-                    .preferredSize(width = 300.dp, height = 50.dp)
-                    .maxLinesHeight(Int.MAX_VALUE, TextStyle.Default)
-                    .textFieldScroll(
-                        orientation = Orientation.Vertical,
-                        remember { scrollerPosition },
-                        value,
-                        VisualTransformation.None,
-                        remember { InteractionState() },
-                        textLayoutResultRef,
-                    )
-            )
-        }
+        rule.setupVerticallyScrollableContent(
+            scrollerPosition = scrollerPosition,
+            text = longText,
+            modifier = Modifier.preferredSize(width = 300.dp, height = 50.dp)
+        )
 
         rule.runOnIdle {
             assertThat(scrollerPosition.maximum).isLessThan(Float.POSITIVE_INFINITY)
@@ -163,29 +146,15 @@
     }
 
     @Test
-    fun testTextField_verticallyScrollable_withLongInput_whenMaxLinesProvided() {
+    fun textFieldScroll_vertical_scrollable_withLongInput_whenMaxLinesProvided() {
         val scrollerPosition = TextFieldScrollerPosition()
-        val value = TextFieldValue(longText)
 
-        rule.setContent {
-            val textLayoutResultRef: Ref<TextLayoutResult?> = remember { Ref() }
-            CoreTextField(
-                value = value,
-                >
-                 textLayoutResultRef.value = it },
-                modifier = Modifier
-                    .preferredWidth(100.dp)
-                    .maxLinesHeight(3, TextStyle.Default)
-                    .textFieldScroll(
-                        orientation = Orientation.Vertical,
-                        remember { scrollerPosition },
-                        value,
-                        VisualTransformation.None,
-                        remember { InteractionState() },
-                        textLayoutResultRef,
-                    )
-            )
-        }
+        rule.setupVerticallyScrollableContent(
+            modifier = Modifier.preferredWidth(100.dp),
+            scrollerPosition = scrollerPosition,
+            text = longText,
+            maxLines = 3
+        )
 
         rule.runOnIdle {
             assertThat(scrollerPosition.maximum).isLessThan(Float.POSITIVE_INFINITY)
@@ -194,30 +163,14 @@
     }
 
     @Test
-    fun testTextField_horizontallyNotScrollable_withShortInput() {
-        val scrollerPosition = TextFieldScrollerPosition()
-        val value = TextFieldValue("text")
+    fun textFieldScroll_horizontal_notScrollable_withShortInput() {
+        val scrollerPosition = TextFieldScrollerPosition(Orientation.Horizontal)
 
-        rule.setContent {
-            val textLayoutResultRef: Ref<TextLayoutResult?> = remember { Ref() }
-            CoreTextField(
-                value = value,
-                >
-                 textLayoutResultRef.value = it },
-                softWrap = false,
-                modifier = Modifier
-                    .preferredSize(width = 300.dp, height = 50.dp)
-                    .maxLinesHeight(1, TextStyle.Default)
-                    .textFieldScroll(
-                        orientation = Orientation.Horizontal,
-                        remember { scrollerPosition },
-                        value,
-                        VisualTransformation.None,
-                        remember { InteractionState() },
-                        textLayoutResultRef,
-                    )
-            )
-        }
+        rule.setupHorizontallyScrollableContent(
+            scrollerPosition = scrollerPosition,
+            text = "text",
+            modifier = Modifier.preferredSize(width = 300.dp, height = 50.dp)
+        )
 
         rule.runOnIdle {
             assertThat(scrollerPosition.maximum).isEqualTo(0f)
@@ -225,28 +178,14 @@
     }
 
     @Test
-    fun testTextField_verticallyNotScrollable_withShortInput() {
+    fun textFieldScroll_vertical_notScrollable_withShortInput() {
         val scrollerPosition = TextFieldScrollerPosition()
-        val value = TextFieldValue("text")
 
-        rule.setContent {
-            val textLayoutResultRef: Ref<TextLayoutResult?> = remember { Ref() }
-            CoreTextField(
-                value = value,
-                >
-                 textLayoutResultRef.value = it },
-                modifier = Modifier
-                    .preferredSize(width = 300.dp, height = 100.dp)
-                    .textFieldScroll(
-                        orientation = Orientation.Vertical,
-                        remember { scrollerPosition },
-                        value,
-                        VisualTransformation.None,
-                        remember { InteractionState() },
-                        textLayoutResultRef,
-                    )
-            )
-        }
+        rule.setupVerticallyScrollableContent(
+            scrollerPosition = scrollerPosition,
+            text = "text",
+            modifier = Modifier.preferredSize(width = 300.dp, height = 100.dp)
+        )
 
         rule.runOnIdle {
             assertThat(scrollerPosition.maximum).isEqualTo(0f)
@@ -256,37 +195,24 @@
     @Test
     @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun testTextField_horizontal_scrolledAndClipped() {
-        val scrollerPosition = TextFieldScrollerPosition()
-        val value = TextFieldValue(longText)
-
+    fun textField_singleLine_scrolledAndClipped() {
         val parentSize = 200
         val textFieldSize = 50
+        val tag = "OuterBox"
 
         with(rule.density) {
             rule.setContent {
-                val textLayoutResultRef: Ref<TextLayoutResult?> = remember { Ref() }
                 Box(
                     Modifier
                         .preferredSize(parentSize.toDp())
                         .background(color = Color.White)
-                        .testTag(TextfieldTag)
+                        .testTag(tag)
                 ) {
-                    CoreTextField(
-                        value = value,
-                        >
-                         textLayoutResultRef.value = it },
-                        softWrap = false,
-                        modifier = Modifier
-                            .preferredSize(textFieldSize.toDp())
-                            .textFieldScroll(
-                                orientation = Orientation.Horizontal,
-                                remember { scrollerPosition },
-                                value,
-                                VisualTransformation.None,
-                                remember { InteractionState() },
-                                textLayoutResultRef
-                            )
+                    ScrollableContent(
+                        modifier = Modifier.preferredSize(textFieldSize.toDp()),
+                        scrollerPosition = TextFieldScrollerPosition(Orientation.Horizontal),
+                        text = longText,
+                        isVertical = false
                     )
                 }
             }
@@ -294,7 +220,7 @@
 
         rule.runOnIdle {}
 
-        rule.onNodeWithTag(TextfieldTag)
+        rule.onNodeWithTag(tag)
             .captureToImage()
             .assertPixels(expectedSize = IntSize(parentSize, parentSize)) { position ->
                 if (position.x > textFieldSize && position.y > textFieldSize) Color.White else null
@@ -304,36 +230,24 @@
     @Test
     @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun testTextField_vertical_scrolledAndClipped() {
-        val scrollerPosition = TextFieldScrollerPosition()
-        val value = TextFieldValue(longText)
-
+    fun textField_multiline_scrolledAndClipped() {
         val parentSize = 200
         val textFieldSize = 50
+        val tag = "OuterBox"
 
         with(rule.density) {
             rule.setContent {
-                val textLayoutResultRef: Ref<TextLayoutResult?> = remember { Ref() }
                 Box(
                     Modifier
                         .preferredSize(parentSize.toDp())
                         .background(color = Color.White)
-                        .testTag(TextfieldTag)
+                        .testTag(tag)
                 ) {
-                    CoreTextField(
-                        value = value,
-                        >
-                         textLayoutResultRef.value = it },
-                        modifier = Modifier
-                            .preferredSize(textFieldSize.toDp())
-                            .textFieldScroll(
-                                orientation = Orientation.Vertical,
-                                remember { scrollerPosition },
-                                value,
-                                VisualTransformation.None,
-                                remember { InteractionState() },
-                                textLayoutResultRef
-                            )
+                    ScrollableContent(
+                        modifier = Modifier.preferredSize(textFieldSize.toDp()),
+                        scrollerPosition = TextFieldScrollerPosition(),
+                        text = longText,
+                        isVertical = true
                     )
                 }
             }
@@ -341,7 +255,7 @@
 
         rule.runOnIdle {}
 
-        rule.onNodeWithTag(TextfieldTag)
+        rule.onNodeWithTag(tag)
             .captureToImage()
             .assertPixels(expectedSize = IntSize(parentSize, parentSize)) { position ->
                 if (position.x > textFieldSize && position.y > textFieldSize) Color.White else null
@@ -349,32 +263,14 @@
     }
 
     @Test
-    fun testTextField_horizontalScroll_swipe_whenLongInput() {
-        val scrollerPosition = TextFieldScrollerPosition()
-        val value = TextFieldValue(longText)
+    fun textFieldScroll_horizontal_swipe_whenLongInput() {
+        val scrollerPosition = TextFieldScrollerPosition(Orientation.Horizontal)
 
-        rule.setContent {
-            val textLayoutResultRef: Ref<TextLayoutResult?> = remember { Ref() }
-
-            CoreTextField(
-                value = value,
-                >
-                 textLayoutResultRef.value = it },
-                softWrap = false,
-                modifier = Modifier
-                    .preferredSize(width = 300.dp, height = 50.dp)
-                    .testTag(TextfieldTag)
-                    .maxLinesHeight(1, TextStyle.Default)
-                    .textFieldScroll(
-                        Orientation.Horizontal,
-                        remember { scrollerPosition },
-                        value,
-                        VisualTransformation.None,
-                        remember { InteractionState() },
-                        textLayoutResultRef
-                    )
-            )
-        }
+        rule.setupHorizontallyScrollableContent(
+            scrollerPosition = scrollerPosition,
+            text = longText,
+            modifier = Modifier.preferredSize(width = 300.dp, height = 50.dp)
+        )
 
         rule.runOnIdle {
             assertThat(scrollerPosition.offset).isEqualTo(0f)
@@ -396,30 +292,14 @@
     }
 
     @Test
-    fun testTextField_verticalScroll_swipe_whenLongInput() {
+    fun textFieldScroll_vertical_swipe_whenLongInput() {
         val scrollerPosition = TextFieldScrollerPosition()
-        val value = TextFieldValue(longText)
 
-        rule.setContent {
-            val textLayoutResultRef: Ref<TextLayoutResult?> = remember { Ref() }
-
-            CoreTextField(
-                value = value,
-                >
-                 textLayoutResultRef.value = it },
-                modifier = Modifier
-                    .preferredSize(width = 300.dp, height = 50.dp)
-                    .testTag(TextfieldTag)
-                    .textFieldScroll(
-                        Orientation.Vertical,
-                        remember { scrollerPosition },
-                        value,
-                        VisualTransformation.None,
-                        remember { InteractionState() },
-                        textLayoutResultRef
-                    )
-            )
-        }
+        rule.setupVerticallyScrollableContent(
+            scrollerPosition = scrollerPosition,
+            text = longText,
+            modifier = Modifier.preferredSize(width = 300.dp, height = 50.dp)
+        )
 
         rule.runOnIdle {
             assertThat(scrollerPosition.offset).isEqualTo(0f)
@@ -441,36 +321,21 @@
     }
 
     @Test
-    fun textFieldScroller_restoresScrollerPosition() {
+    fun textFieldScroll_restoresScrollerPosition() {
         val restorationTester = StateRestorationTester(rule)
-        var scrollerPosition = TextFieldScrollerPosition()
-        val value = TextFieldValue(longText)
+        var scrollerPosition: TextFieldScrollerPosition? = null
 
         restorationTester.setContent {
-            val textLayoutResultRef: Ref<TextLayoutResult?> = remember { Ref() }
-
             scrollerPosition = rememberSavedInstanceState(
                 saver = TextFieldScrollerPosition.Saver
             ) {
-                TextFieldScrollerPosition()
+                TextFieldScrollerPosition(Orientation.Horizontal)
             }
-            CoreTextField(
-                value = value,
-                >
-                 textLayoutResultRef.value = it },
-                softWrap = false,
-                modifier = Modifier
-                    .preferredSize(width = 300.dp, height = 50.dp)
-                    .testTag(TextfieldTag)
-                    .maxLinesHeight(1, TextStyle.Default)
-                    .textFieldScroll(
-                        Orientation.Horizontal,
-                        scrollerPosition,
-                        value,
-                        VisualTransformation.None,
-                        remember { InteractionState() },
-                        textLayoutResultRef
-                    )
+            ScrollableContent(
+                modifier = Modifier.preferredSize(width = 300.dp, height = 50.dp),
+                scrollerPosition = scrollerPosition!!,
+                text = longText,
+                isVertical = false
             )
         }
 
@@ -478,53 +343,41 @@
             .performGesture { swipeLeft() }
 
         val swipePosition = rule.runOnIdle {
-            scrollerPosition.offset
+            scrollerPosition!!.offset
         }
         assertThat(swipePosition).isGreaterThan(0f)
 
         rule.runOnIdle {
             scrollerPosition = TextFieldScrollerPosition()
-            assertThat(scrollerPosition.offset).isEqualTo(0f)
+            assertThat(scrollerPosition!!.offset).isEqualTo(0f)
         }
 
         restorationTester.emulateSavedInstanceStateRestore()
 
         rule.runOnIdle {
-            assertThat(scrollerPosition.offset).isEqualTo(swipePosition)
+            assertThat(scrollerPosition!!.offset).isEqualTo(swipePosition)
         }
     }
 
     @Test
-    fun testInspectorValue() {
-        val position = TextFieldScrollerPosition(initial = 10f)
-        val orientation = Orientation.Vertical
-        val value = TextFieldValue()
+    fun textFieldScrollable_testInspectorValue() {
+        val position = TextFieldScrollerPosition(Orientation.Vertical, 10f)
+        val interactionState = InteractionState()
         rule.setContent {
-            val modifier = Modifier.textFieldScroll(
-                orientation,
-                position,
-                value,
-                VisualTransformation.None,
-                remember { InteractionState() },
-                Ref(),
-                true
-            ) as InspectableValue
-            assertThat(modifier.nameFallback).isEqualTo("textFieldScroll")
+            val modifier =
+                Modifier.textFieldScrollable(position, interactionState) as InspectableValue
+            assertThat(modifier.nameFallback).isEqualTo("textFieldScrollable")
             assertThat(modifier.valueOverride).isNull()
             assertThat(modifier.inspectableElements.map { it.name }.asIterable()).containsExactly(
-                "orientation",
                 "scrollerPosition",
-                "textFieldValue",
-                "visualTransformation",
                 "interactionState",
-                "textLayoutResult",
                 "enabled"
             )
         }
     }
 
     @Test
-    fun testNestedScrolling() {
+    fun textFieldScroll_testNestedScrolling() {
         val size = 300.dp
         val text = """
             First Line
@@ -532,7 +385,6 @@
             Third Line
             Fourth Line
         """.trimIndent()
-        val value = TextFieldValue(text)
 
         val textFieldScrollPosition = TextFieldScrollerPosition()
         val scrollerPosition = ScrollState(
@@ -542,28 +394,16 @@
         )
 
         rule.setContent {
-            val textLayoutResultRef: Ref<TextLayoutResult?> = remember { Ref() }
             Column(
                 Modifier
                     .preferredSize(size)
                     .verticalScroll(scrollerPosition)
             ) {
-                CoreTextField(
-                    value = value,
-                    >
-                     textLayoutResultRef.value = it },
-                    modifier = Modifier
-                        .preferredSize(size, 50.dp)
-                        .testTag(TextfieldTag)
-                        .textFieldScroll(
-                            Orientation.Vertical,
-                            remember { textFieldScrollPosition },
-                            value,
-                            VisualTransformation.None,
-                            remember { InteractionState() },
-                            textLayoutResultRef
-                        ),
-                    textStyle = TextStyle(fontSize = 20.sp)
+                ScrollableContent(
+                    modifier = Modifier.preferredSize(size, 50.dp),
+                    scrollerPosition = textFieldScrollPosition,
+                    text = text,
+                    isVertical = true
                 )
                 Box(Modifier.preferredSize(size))
                 Box(Modifier.preferredSize(size))
@@ -593,8 +433,72 @@
 
         rule.runOnIdle {
             assertThat(textFieldScrollPosition.offset).isGreaterThan(0f)
-            assertThat(textFieldScrollPosition.offset).isEqualTo(textFieldScrollPosition.maximum)
+            assertThat(textFieldScrollPosition.offset)
+                .isWithin(0.5f).of(textFieldScrollPosition.maximum)
             assertThat(scrollerPosition.value).isGreaterThan(0f)
         }
     }
+
+    private fun ComposeTestRule.setupHorizontallyScrollableContent(
+        scrollerPosition: TextFieldScrollerPosition,
+        text: String,
+        modifier: Modifier = Modifier
+    ) {
+        setContent {
+            ScrollableContent(
+                scrollerPosition = scrollerPosition,
+                text = text,
+                isVertical = false,
+                modifier = modifier,
+                maxLines = 1
+            )
+        }
+    }
+
+    private fun ComposeTestRule.setupVerticallyScrollableContent(
+        scrollerPosition: TextFieldScrollerPosition,
+        text: String,
+        modifier: Modifier = Modifier,
+        maxLines: Int = Int.MAX_VALUE
+    ) {
+        setContent {
+            ScrollableContent(
+                scrollerPosition = scrollerPosition,
+                text = text,
+                isVertical = true,
+                modifier = modifier,
+                maxLines = maxLines
+            )
+        }
+    }
+
+    @Composable
+    private fun ScrollableContent(
+        modifier: Modifier,
+        scrollerPosition: TextFieldScrollerPosition,
+        text: String,
+        isVertical: Boolean,
+        maxLines: Int = Int.MAX_VALUE
+    ) {
+        val textLayoutResultRef: Ref<TextLayoutResultProxy?> = remember { Ref() }
+        val resolvedMaxLines = if (isVertical) maxLines else 1
+
+        BasicText(
+            text = text,
+            >
+                textLayoutResultRef.value = TextLayoutResultProxy(it)
+            },
+            softWrap = isVertical,
+            modifier = modifier
+                .testTag(TextfieldTag)
+                .maxLinesHeight(resolvedMaxLines, TextStyle.Default)
+                .textFieldScrollable(scrollerPosition)
+                .textFieldScroll(
+                    remember { scrollerPosition },
+                    TextFieldValue(text),
+                    VisualTransformation.None,
+                    { textLayoutResultRef.value }
+                )
+        )
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
index 5aa9fe7..ca6be5a 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
@@ -21,11 +21,18 @@
 
 import android.os.Build
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.Interaction
+import androidx.compose.foundation.InteractionState
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.preferredSize
+import androidx.compose.foundation.text.BasicText
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.runtime.Composable
@@ -40,6 +47,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.isFocused
 import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.layout.onGloballyPositioned
@@ -55,6 +63,7 @@
 import androidx.compose.ui.test.assertHasClickAction
 import androidx.compose.ui.test.assertTextEquals
 import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.click
 import androidx.compose.ui.test.hasImeAction
 import androidx.compose.ui.test.hasSetTextAction
 import androidx.compose.ui.test.isFocused
@@ -63,6 +72,7 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performGesture
 import androidx.compose.ui.test.performSemanticsAction
 import androidx.compose.ui.test.performTextClearance
 import androidx.compose.ui.test.performTextInput
@@ -77,6 +87,7 @@
 import androidx.compose.ui.text.input.TextFieldValue.Companion.Saver
 import androidx.compose.ui.text.input.TextInputService
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
@@ -636,4 +647,42 @@
             assertThat(lastSeenText).isEqualTo("")
         }
     }
+
+    @Test
+    fun decorationBox_clickable() {
+        val interactionState = InteractionState()
+        rule.setContent {
+            Column {
+                BasicTextField(
+                    value = "test",
+                    >
+                    textStyle = TextStyle(fontSize = 2.sp),
+                    modifier = Modifier.height(100.dp).fillMaxWidth(),
+                    decorationBox = {
+                        // the core text field is at the very bottom
+                        Column {
+                            BasicText("Label", Modifier.testTag("label"))
+                            Spacer(Modifier.weight(1f))
+                            it()
+                        }
+                    },
+                    interactionState = interactionState
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(interactionState.contains(Interaction.Focused)).isFalse()
+        }
+
+        // click outside core text field area
+        rule.onNodeWithTag("label")
+            .performGesture {
+                click(Offset.Zero)
+            }
+
+        rule.runOnIdle {
+            assertThat(interactionState.contains(Interaction.Focused)).isTrue()
+        }
+    }
 }
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 32e24c0..72f9598 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
@@ -33,6 +33,8 @@
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.graphics.vector.rememberVectorPainter
 import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
 
 /**
  * A composable that lays out and draws a given [ImageBitmap]. This will attempt to
@@ -49,13 +51,17 @@
  * overload that consumes a [Painter] parameter shown in this sample
  * @sample androidx.compose.foundation.samples.ImagePainterSubsectionSample
  *
- * @param bitmap The [ImageBitmap] to draw.
+ * @param bitmap The [ImageBitmap] to draw
+ * @param contentDescription text used by accessibility services to describe what this image
+ * represents. This should always be provided unless this image is used for decorative purposes,
+ * and does not represent a meaningful action that a user can take. This text should be
+ * localized, such as by using [androidx.compose.ui.res.stringResource] or similar
  * @param modifier Modifier used to adjust the layout algorithm or draw decoration content (ex.
  * background)
  * @param alignment Optional alignment parameter used to place the [ImageBitmap] in the given
- * bounds defined by the width and height.
+ * 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 [ImageBitmap].
+ * 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
@@ -64,6 +70,7 @@
 @Composable
 inline fun Image(
     bitmap: ImageBitmap,
+    contentDescription: String?,
     modifier: Modifier = Modifier,
     alignment: Alignment = Alignment.Center,
     contentScale: ContentScale = ContentScale.Fit,
@@ -73,6 +80,7 @@
     val imagePainter = remember(bitmap) { ImagePainter(bitmap) }
     Image(
         painter = imagePainter,
+        contentDescription = contentDescription,
         modifier = modifier,
         alignment = alignment,
         contentScale = contentScale,
@@ -90,13 +98,17 @@
  *
  * @sample androidx.compose.foundation.samples.ImageVectorSample
  *
- * @param imageVector The [ImageVector] to draw.
+ * @param imageVector The [ImageVector] to draw
+ * @param contentDescription text used by accessibility services to describe what this image
+ * represents. This should always be provided unless this image is used for decorative purposes,
+ * and does not represent a meaningful action that a user can take. This text should be
+ * localized, such as by using [androidx.compose.ui.res.stringResource] or similar
  * @param modifier Modifier used to adjust the layout algorithm or draw decoration content (ex.
  * background)
  * @param alignment Optional alignment parameter used to place the [ImageVector] in the given
- * bounds defined by the width and height.
+ * 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 [ImageVector].
+ * if the bounds are a different size from the intrinsic size of the [ImageVector]
  * @param alpha Optional opacity to be applied to the [ImageVector] when it is rendered onscreen
  * @param colorFilter Optional ColorFilter to apply for the [ImageVector] when it is rendered
  * onscreen
@@ -105,6 +117,7 @@
 @Composable
 inline fun Image(
     imageVector: ImageVector,
+    contentDescription: String?,
     modifier: Modifier = Modifier,
     alignment: Alignment = Alignment.Center,
     contentScale: ContentScale = ContentScale.Fit,
@@ -112,6 +125,7 @@
     colorFilter: ColorFilter? = null
 ) = Image(
     painter = rememberVectorPainter(imageVector),
+    contentDescription = contentDescription,
     modifier = modifier,
     alignment = alignment,
     contentScale = contentScale,
@@ -132,12 +146,16 @@
  * @sample androidx.compose.foundation.samples.ImagePainterSample
  *
  * @param painter to draw
+ * @param contentDescription text used by accessibility services to describe what this image
+ * represents. This should always be provided unless this image is used for decorative purposes,
+ * and does not represent a meaningful action that a user can take. This text should be
+ * localized, such as by using [androidx.compose.ui.res.stringResource] or similar
  * @param modifier Modifier used to adjust the layout algorithm or draw decoration content (ex.
  * background)
  * @param alignment Optional alignment parameter used to place the [Painter] 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 [Painter].
+ * if the bounds are a different size from the intrinsic size of the [Painter]
  * @param alpha Optional opacity to be applied to the [Painter] when it is rendered onscreen
  * the default renders the [Painter] completely opaque
  * @param colorFilter Optional colorFilter to apply for the [Painter] when it is rendered onscreen
@@ -145,17 +163,24 @@
 @Composable
 fun Image(
     painter: Painter,
+    contentDescription: String?,
     modifier: Modifier = Modifier,
     alignment: Alignment = Alignment.Center,
     contentScale: ContentScale = ContentScale.Fit,
     alpha: Float = DefaultAlpha,
     colorFilter: ColorFilter? = null
 ) {
+    val semantics = if (contentDescription != null) {
+        Modifier.semantics { this.contentDescription = contentDescription }
+    } else {
+        Modifier
+    }
+
     // Explicitly use a simple Layout implementation here as Spacer squashes any non fixed
     // constraint with zero
     Layout(
         emptyContent(),
-        modifier.clipToBounds().paint(
+        modifier.then(semantics).clipToBounds().paint(
             painter,
             alignment = alignment,
             contentScale = contentScale,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
index 1c2db5d..a3428b9 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
@@ -22,12 +22,9 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.node.Ref
 import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.SoftwareKeyboardController
@@ -67,9 +64,14 @@
  * composable is designed to be used when a custom implementation for different design system is
  * needed.
  *
- * For example, if you need to include a hint in your TextField you can write a composable as below:
+ * For example, if you need to include a placeholder in your TextField, you can write a composable
+ * using the decoration box like this:
  * @sample androidx.compose.foundation.samples.PlaceholderBasicTextFieldSample
  *
+ * If you want to add decorations to your text field, such as icon or similar, and increase the
+ * hit target area, use the decoration box:
+ * @sample androidx.compose.foundation.samples.TextFieldWithIconSample
+ *
  * @param value the input [String] text to be shown in the text field
  * @param onValueChange the callback that is triggered when the input service updates the text. An
  * updated text comes as a parameter of the callback
@@ -77,8 +79,8 @@
  * @param enabled controls the enabled state of the [BasicTextField]. When `false`, the text
  * field will be neither editable nor focusable, the input of the text field will not be selectable
  * @param readOnly controls the editable state of the [BasicTextField]. When `true`, the text
- * fields will not be editable but otherwise operable. Read-only text fields are usually used to
- * display the pre-filled text that user cannot edit
+ * field can not be modified, however, a user can focus it and copy text from it. Read-only text
+ * fields are usually used to display pre-filled forms that user can not edit
  * @param textStyle Style configuration that applies at character level such as color, font etc.
  * @param keyboardOptions software keyboard options that contains configuration such as
  * [KeyboardType] and [ImeAction].
@@ -104,6 +106,12 @@
  * if you want to read the [InteractionState] and customize the appearance / behavior of this
  * TextField in different [Interaction]s.
  * @param cursorColor Color of the cursor. If [Color.Unspecified], there will be no cursor drawn
+ * @param decorationBox Composable lambda that allows to add decorations around text field, such
+ * as icon, placeholder, helper messages or similar, and automatically increase the hit target area
+ * of the text field. To allow you to control the placement of the inner text field relative to your
+ * decorations, the text field implementation will pass in a framework-controlled composable
+ * parameter "innerTextField" to the decorationBox lambda you provide. You must call
+ * innerTextField exactly once.
  */
 @OptIn(ExperimentalTextApi::class)
 @Composable
@@ -122,7 +130,9 @@
     onTextLayout: (TextLayoutResult) -> Unit = {},
     onTextInputStarted: (SoftwareKeyboardController) -> Unit = {},
     interactionState: InteractionState = remember { InteractionState() },
-    cursorColor: Color = Color.Black
+    cursorColor: Color = Color.Black,
+    decorationBox: @Composable (innerTextField: @Composable () -> Unit) -> Unit =
+        @Composable { innerTextField -> innerTextField() }
 ) {
     var textFieldValueState by remember { mutableStateOf(TextFieldValue(text = value)) }
     val textFieldValue = textFieldValueState.copy(text = value)
@@ -147,7 +157,8 @@
         >
         cursorColor = cursorColor,
         interactionState = interactionState,
-        singleLine = singleLine
+        singleLine = singleLine,
+        decorationBox = decorationBox
     )
 }
 
@@ -178,9 +189,15 @@
  * composable is designed to be used when a custom implementation for different design system is
  * needed.
  *
- * For example, if you need to include a hint in your TextField you can write a composable as below:
+ * For example, if you need to include a placeholder in your TextField, you can write a composable
+ * using the decoration box like this:
  * @sample androidx.compose.foundation.samples.PlaceholderBasicTextFieldSample
  *
+ *
+ * If you want to add decorations to your text field, such as icon or similar, and increase the
+ * hit target area, use the decoration box:
+ * @sample androidx.compose.foundation.samples.TextFieldWithIconSample
+ *
  * @param value The [androidx.compose.ui.text.input.TextFieldValue] to be shown in the
  * [BasicTextField].
  * @param onValueChange Called when the input service updates the values in [TextFieldValue].
@@ -188,8 +205,8 @@
  * @param enabled controls the enabled state of the [BasicTextField]. When `false`, the text
  * field will be neither editable nor focusable, the input of the text field will not be selectable
  * @param readOnly controls the editable state of the [BasicTextField]. When `true`, the text
- * fields will not be editable but otherwise operable. Read-only text fields are usually used to
- * display the pre-filled text that user cannot edit
+ * field can not be modified, however, a user can focus it and copy text from it. Read-only text
+ * fields are usually used to display pre-filled forms that user can not edit
  * @param textStyle Style configuration that applies at character level such as color, font etc.
  * @param keyboardOptions software keyboard options that contains configuration such as
  * [KeyboardType] and [ImeAction].
@@ -215,6 +232,12 @@
  * if you want to read the [InteractionState] and customize the appearance / behavior of this
  * TextField in different [Interaction]s.
  * @param cursorColor Color of the cursor. If [Color.Unspecified], there will be no cursor drawn
+ * @param decorationBox Composable lambda that allows to add decorations around text field, such
+ * as icon, placeholder, helper messages or similar, and automatically increase the hit target area
+ * of the text field. To allow you to control the placement of the inner text field relative to your
+ * decorations, the text field implementation will pass in a framework-controlled composable
+ * parameter "innerTextField" to the decorationBox lambda you provide. You must call
+ * innerTextField exactly once.
  */
 @Composable
 @OptIn(InternalTextApi::class, ExperimentalTextApi::class)
@@ -233,60 +256,26 @@
     onTextLayout: (TextLayoutResult) -> Unit = {},
     onTextInputStarted: (SoftwareKeyboardController) -> Unit = {},
     interactionState: InteractionState = remember { InteractionState() },
-    cursorColor: Color = Color.Black
+    cursorColor: Color = Color.Black,
+    decorationBox: @Composable (innerTextField: @Composable () -> Unit) -> Unit =
+        @Composable { innerTextField -> innerTextField() }
 ) {
-    val orientation = if (singleLine) Orientation.Horizontal else Orientation.Vertical
-    val scrollerPosition = rememberSavedInstanceState(saver = TextFieldScrollerPosition.Saver) {
-        TextFieldScrollerPosition()
-    }
-
-    // We use it to get the cursor position
-    val textLayoutResult: Ref<TextLayoutResult?> = remember { Ref() }
-
-    val textFieldModifier = modifier
-        .maxLinesHeight(if (singleLine) 1 else maxLines, textStyle)
-        .textFieldScroll(
-            orientation,
-            scrollerPosition,
-            value,
-            visualTransformation,
-            interactionState,
-            textLayoutResult,
-            enabled
-        )
-
-    if (enabled && !readOnly) {
-        CoreTextField(
-            value = value,
-            >
-            textStyle = textStyle,
-            >
-            visualTransformation = visualTransformation,
-            >
-                textLayoutResult.value = it
-                onTextLayout(it)
-            },
-            interactionState = interactionState,
-            >
-            cursorColor = cursorColor,
-            imeOptions = keyboardOptions.toImeOptions(singleLine = singleLine),
-            softWrap = !singleLine,
-            modifier = textFieldModifier
-        )
-    } else {
-        InactiveTextField(
-            value = value,
-            modifier = textFieldModifier,
-            enabled = enabled,
-            textStyle = textStyle,
-            singleLine = singleLine,
-            maxLines = maxLines,
-            visualTransformation = visualTransformation,
-            >
-                textLayoutResult.value = it
-                onTextLayout(it)
-            },
-            interactionState = interactionState
-        )
-    }
+    CoreTextField(
+        value = value,
+        >
+        modifier = modifier,
+        textStyle = textStyle,
+        >
+        visualTransformation = visualTransformation,
+        >
+        interactionState = interactionState,
+        >
+        cursorColor = cursorColor,
+        imeOptions = keyboardOptions.toImeOptions(singleLine = singleLine),
+        softWrap = !singleLine,
+        maxLines = if (singleLine) 1 else maxLines,
+        decorationBox = decorationBox,
+        enabled = enabled,
+        readOnly = readOnly
+    )
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
index 444f974..a0ac2a4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
@@ -19,7 +19,7 @@
 
 import androidx.compose.foundation.Interaction
 import androidx.compose.foundation.InteractionState
-import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.text.selection.TextFieldSelectionHandle
 import androidx.compose.foundation.text.selection.TextFieldSelectionManager
 import androidx.compose.foundation.text.selection.isSelectionHandleInVisibleBound
@@ -30,16 +30,13 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.onDispose
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.focus.isFocused
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.gesture.dragGestureFilter
-import androidx.compose.ui.gesture.longPressDragGestureFilter
-import androidx.compose.ui.gesture.tapGestureFilter
+import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
@@ -60,6 +57,7 @@
 import androidx.compose.ui.selection.SimpleLayout
 import androidx.compose.ui.semantics.copyText
 import androidx.compose.ui.semantics.cutText
+import androidx.compose.ui.semantics.disabled
 import androidx.compose.ui.semantics.getTextLayoutResult
 import androidx.compose.ui.semantics.imeAction
 import androidx.compose.ui.semantics.onClick
@@ -83,7 +81,9 @@
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.ImeOptions
 import androidx.compose.ui.text.input.NO_SESSION
+import androidx.compose.ui.text.input.OffsetMapping
 import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.input.TextInputService
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.Density
 import kotlin.math.max
@@ -95,6 +95,11 @@
  * This composable provides basic text editing functionality, however does not include any
  * decorations such as borders, hints/placeholder.
  *
+ * If the editable text is larger than the size of the container, the vertical scrolling
+ * behaviour will be automatically applied. To enable a single line behaviour with horizontal
+ * scrolling instead, set the [maxLines] parameter to 1, [softWrap] to false, and
+ * [ImeOptions.singleLine] to true.
+ *
  * Whenever the user edits the text, [onValueChange] is called with the most up to date state
  * represented by [TextFieldValue]. [TextFieldValue] contains the text entered by user, as well
  * as selection, cursor and text composition information. Please check [TextFieldValue] for the
@@ -131,6 +136,20 @@
  * @param cursorColor Color of the cursor. If [Color.Unspecified], there will be no cursor drawn
  * @param softWrap Whether the text should break at soft line breaks. If false, the glyphs in the
  * text will be positioned as if there was unlimited horizontal space.
+ * @param maxLines The maximum height in terms of maximum number of visible lines. Should be
+ * equal or greater than 1.
+ * @param imeOptions Contains different IME configuration options.
+ * @param enabled controls the enabled state of the text field. When `false`, the text
+ * field will be neither editable nor focusable, the input of the text field will not be selectable
+ * @param readOnly controls the editable state of the [CoreTextField]. When `true`, the text
+ * field can not be modified, however, a user can focus it and copy text from it. Read-only text
+ * fields are usually used to display pre-filled forms that user can not edit
+ * @param decorationBox Composable lambda that allows to add decorations around text field, such
+ * as icon, placeholder, helper messages or similar, and automatically increase the hit target area
+ * of the text field. To allow you to control the placement of the inner text field relative to your
+ * decorations, the text field implementation will pass in a framework-controlled composable
+ * parameter "innerTextField" to the decorationBox lambda you provide. You must call
+ * innerTextField exactly once.
  */
 @Composable
 @OptIn(
@@ -150,7 +169,12 @@
     interactionState: InteractionState? = null,
     cursorColor: Color = Color.Unspecified,
     softWrap: Boolean = true,
-    imeOptions: ImeOptions = ImeOptions.Default
+    maxLines: Int = Int.MAX_VALUE,
+    imeOptions: ImeOptions = ImeOptions.Default,
+    enabled: Boolean = true,
+    readOnly: Boolean = false,
+    decorationBox: @Composable (innerTextField: @Composable () -> Unit) -> Unit =
+        @Composable { innerTextField -> innerTextField() }
 ) {
     // If developer doesn't pass new value to TextField, recompose won't happen but internal state
     // and IME may think it is updated. To fix this inconsistent state, enforce recompose.
@@ -158,11 +182,20 @@
     val focusRequester = FocusRequester()
 
     // Ambients
-    val textInputService = AmbientTextInputService.current
+    // If the text field is disabled or read-only, we should not deal with the input service
+    val textInputService = if (!enabled || readOnly) null else AmbientTextInputService.current
     val density = AmbientDensity.current
     val resourceLoader = AmbientFontLoader.current
     val selectionBackgroundColor = AmbientTextSelectionColors.current.backgroundColor
 
+    // Scroll state
+    val singleLine = maxLines == 1 && !softWrap && imeOptions.singleLine
+    val orientation = if (singleLine) Orientation.Horizontal else Orientation.Vertical
+    val scrollerPosition = rememberSavedInstanceState(
+        orientation,
+        saver = TextFieldScrollerPosition.Saver
+    ) { TextFieldScrollerPosition(orientation) }
+
     // State
     val transformedText = remember(value, visualTransformation) {
         val transformed = visualTransformation.filter(value.annotatedString)
@@ -215,70 +248,44 @@
     manager.textToolbar = AmbientTextToolbar.current
     manager.hapticFeedBack = AmbientHapticFeedback.current
     manager.focusRequester = focusRequester
+    manager.editable = !readOnly
 
-    val  {
+    // Focus
+    val focusModifier = Modifier.textFieldFocusModifier(
+        enabled = enabled,
+        focusRequester = focusRequester,
+        interactionState = interactionState
+    ) {
         if (state.hasFocus == it.isFocused) {
-            return@onFocusChanged
+            return@textFieldFocusModifier
         }
-
         state.hasFocus = it.isFocused
 
-        if (it.isFocused) {
-            state.inputSession = TextFieldDelegate.onFocus(
+        if (textInputService != null) {
+            notifyTextInputServiceOnFocusChange(
                 textInputService,
+                state,
                 value,
-                state.processor,
                 imeOptions,
                 onValueChangeWrapper,
-                onImeActionPerformedWrapper
+                onImeActionPerformedWrapper,
+                onTextInputStarted,
+                offsetMapping
             )
-            if (state.inputSession != NO_SESSION && textInputService != null) {
-                onTextInputStarted(
-                    SoftwareKeyboardController(
-                        textInputService,
-                        state.inputSession
-                    )
-                )
-            }
-            state.layoutCoordinates?.let { coords ->
-                textInputService?.let { textInputService ->
-                    state.layoutResult?.let { layoutResult ->
-                        TextFieldDelegate.notifyFocusedRect(
-                            value,
-                            state.textDelegate,
-                            layoutResult,
-                            coords,
-                            textInputService,
-                            state.inputSession,
-                            state.hasFocus,
-                            offsetMapping
-                        )
-                    }
-                }
-            }
-        } else {
-            TextFieldDelegate.onBlur(
-                textInputService,
-                state.inputSession,
-                state.processor,
-                false,
-                onValueChangeWrapper
-            )
-            manager.deselect()
         }
+        if (!it.isFocused) manager.deselect()
     }
 
-    val focusRequestTapModifier = Modifier.tapGestureFilter {
-        if (!state.hasFocus) {
-            focusRequester.requestFocus()
-        } else {
-            // if already focused make sure tap request keyboard.
-            textInputService?.showSoftwareKeyboard(state.inputSession)
+    val focusRequestTapModifier = Modifier.focusRequestTapModifier(
+        enabled = enabled,
+        >
+            tapToFocus(state, focusRequester, textInputService, !readOnly)
         }
-    }
+    )
 
     val dragPositionGestureModifier = Modifier.dragPositionGestureFilter(
         interactionState = interactionState,
+        enabled = enabled,
         >
             if (state.hasFocus) {
                 state.selectionIsOn = false
@@ -300,15 +307,15 @@
         }
     )
 
+    val selectionModifier =
+        Modifier.longPressDragGestureFilter(manager.touchSelectionObserver, enabled)
+
     val pointerModifier = if (isMouseInput) {
-        Modifier.dragGestureFilter(
+        Modifier.mouseDragGestureFilter(
             manager.mouseSelectionObserver( focusRequester.requestFocus() }),
-            startDragImmediately = true
+            enabled = enabled
         )
     } else {
-        val selectionModifier = Modifier.longPressDragGestureFilter(
-            manager.touchSelectionObserver
-        )
         dragPositionGestureModifier
             .then(selectionModifier)
             .then(focusRequestTapModifier)
@@ -321,7 +328,7 @@
                     canvas,
                     value,
                     offsetMapping,
-                    layoutResult,
+                    layoutResult.value,
                     state.selectionPaint
                 )
             }
@@ -344,7 +351,7 @@
                 TextFieldDelegate.notifyFocusedRect(
                     value,
                     state.textDelegate,
-                    layoutResult,
+                    layoutResult.value,
                     it,
                     textInputService,
                     state.inputSession,
@@ -353,6 +360,7 @@
                 )
             }
         }
+        state.layoutResult?.innerTextFieldCoordinates = it
     }
 
     val semanticsModifier = Modifier.semantics {
@@ -360,9 +368,10 @@
         this.imeAction = imeOptions.imeAction
         this.text = value.annotatedString
         this.textSelectionRange = value.selection
+        if (!enabled) this.disabled()
         getTextLayoutResult {
             if (state.layoutResult != null) {
-                it.add(state.layoutResult!!)
+                it.add(state.layoutResult!!.value)
                 true
             } else {
                 false
@@ -373,7 +382,9 @@
             true
         }
         setSelection { start, end, traversalMode ->
-            if (start == value.selection.start && end == value.selection.end) {
+            if (!enabled) {
+                false
+            } else if (start == value.selection.start && end == value.selection.end) {
                 false
             } else if (start.coerceAtMost(end) >= 0 &&
                 start.coerceAtLeast(end) <= value.annotatedString.length
@@ -393,11 +404,9 @@
             }
         }
         onClick {
-            if (!state.hasFocus) {
-                focusRequester.requestFocus()
-            } else {
-                textInputService?.showSoftwareKeyboard(state.inputSession)
-            }
+            // according to the documentation, we still need to provide proper semantics actions
+            // even if the state is 'disabled'
+            tapToFocus(state, focusRequester, textInputService, !readOnly)
             true
         }
         onLongClick {
@@ -409,99 +418,88 @@
                 manager.copy()
                 true
             }
-            cutText {
-                manager.cut()
+            if (enabled && !readOnly) {
+                cutText {
+                    manager.cut()
+                    true
+                }
+            }
+        }
+        if (enabled && !readOnly) {
+            pasteText {
+                manager.paste()
                 true
             }
         }
-        pasteText {
-            manager.paste()
-            true
-        }
     }
 
-    val cursorModifier = Modifier.cursor(state, value, offsetMapping, cursorColor)
+    val cursorModifier =
+        Modifier.cursor(state, value, offsetMapping, cursorColor, enabled && !readOnly)
 
     onDispose { manager.hideSelectionToolbar() }
 
-    val modifiers = modifier
-        .focusRequester(focusRequester)
-        .then(onFocusChanged)
-        .then(cursorModifier)
+    // Modifiers that should be applied to the outer text field container. Usually those include
+    // gesture and semantics modifiers.
+    val decorationBoxModifier = modifier
         .then(pointerModifier)
-        .then(drawModifier)
-        .then(onPositionedModifier)
+        .textFieldScrollable(scrollerPosition, interactionState, enabled)
         .then(semanticsModifier)
-        .textFieldMinSize(textStyle)
-        .textFieldKeyboardModifier(manager)
-        .focusable(interactionState = interactionState)
-
-    SimpleLayout(modifiers) {
-        Layout(emptyContent()) { _, constraints ->
-            TextFieldDelegate.layout(
-                state.textDelegate,
-                constraints,
-                layoutDirection,
-                state.layoutResult
-            ).let { (width, height, result) ->
-                if (state.layoutResult != result) {
-                    state.layoutResult = result
-                    onTextLayout(result)
-                }
-                layout(
-                    width,
-                    height,
-                    mapOf(
-                        FirstBaseline to result.firstBaseline.roundToInt(),
-                        LastBaseline to result.lastBaseline.roundToInt()
-                    )
-                ) {}
-            }
+        .then(focusModifier)
+        .onGloballyPositioned {
+            state.layoutResult?.decorationBoxCoordinates = it
         }
 
-        if (
-            state.hasFocus &&
-            state.selectionIsOn &&
-            state.layoutCoordinates != null &&
-            state.layoutCoordinates!!.isAttached &&
-            !isMouseInput
-        ) {
-            manager.state?.layoutResult?.let {
-                if (!value.selection.collapsed) {
-                    val startOffset = offsetMapping.originalToTransformed(value.selection.start)
-                    val endOffset = offsetMapping.originalToTransformed(value.selection.end)
-                    val startDirection = it.getBidiRunDirection(startOffset)
-                    val endDirection = it.getBidiRunDirection(max(endOffset - 1, 0))
-                    val directions = Pair(startDirection, endDirection)
-                    if (state.showSelectionHandleStart) {
-                        TextFieldSelectionHandle(
-                            isStartHandle = true,
-                            directions = directions,
-                            manager = manager
-                        )
-                    }
-                    if (state.showSelectionHandleEnd) {
-                        TextFieldSelectionHandle(
-                            isStartHandle = false,
-                            directions = directions,
-                            manager = manager
-                        )
+    Box(modifier = decorationBoxModifier, propagateMinConstraints = true) {
+        decorationBox {
+            // Modifiers applied directly to the internal input field implementation. In general,
+            // these will most likely include draw, layout and IME related modifiers.
+            val coreTextFieldModifier = Modifier
+                .maxLinesHeight(maxLines, textStyle)
+                .textFieldScroll(
+                    scrollerPosition,
+                    value,
+                    visualTransformation,
+                    { state.layoutResult }
+                )
+                .then(cursorModifier)
+                .then(drawModifier)
+                .then(onPositionedModifier)
+                .textFieldMinSize(textStyle)
+                .textFieldKeyboardModifier(manager)
+
+            SimpleLayout(coreTextFieldModifier) {
+                Layout(emptyContent()) { _, constraints ->
+                    TextFieldDelegate.layout(
+                        state.textDelegate,
+                        constraints,
+                        layoutDirection,
+                        state.layoutResult?.value
+                    ).let { (width, height, result) ->
+                        if (state.layoutResult?.value != result) {
+                            state.layoutResult = TextLayoutResultProxy(result)
+                            onTextLayout(result)
+                        }
+                        layout(
+                            width,
+                            height,
+                            mapOf(
+                                FirstBaseline to result.firstBaseline.roundToInt(),
+                                LastBaseline to result.lastBaseline.roundToInt()
+                            )
+                        ) {}
                     }
                 }
 
-                manager.state?.let {
-                    // If in selection mode (when the floating toolbar is shown) a new symbol
-                    // from the keyboard is entered, text field should enter the editing mode
-                    // instead.
-                    if (manager.isTextChanged()) it.showFloatingToolbar = false
-                    if (it.hasFocus) {
-                        if (it.showFloatingToolbar) manager.showSelectionToolbar()
-                        else manager.hideSelectionToolbar()
-                    }
-                }
+                SelectionToolbarAndHandles(
+                    manager = manager,
+                    show = enabled &&
+                        state.hasFocus &&
+                        state.selectionIsOn &&
+                        state.layoutCoordinates != null &&
+                        state.layoutCoordinates!!.isAttached &&
+                        !isMouseInput
+                )
             }
-        } else {
-            manager.hideSelectionToolbar()
         }
     }
 }
@@ -524,8 +522,18 @@
     /** The last layout coordinates for the Text's layout, used by selection */
     var layoutCoordinates: LayoutCoordinates? = null
 
-    /** The latest TextLayoutResult calculated in the measure block */
-    var layoutResult: TextLayoutResult? = null
+    /**
+     * You should be using proxy type [TextLayoutResultProxy] if you need to translate touch
+     * offset into text's coordinate system. For example, if you add a gesture on top of the
+     * decoration box and want to know the character in text for the given touch offset on
+     * decoration box.
+     * When you don't need to shift the touch offset, you should be using `layoutResult.value`
+     * which omits the proxy and calls the layout result directly. This is needed when you work
+     * with the text directly, and not the decoration box. For example, cursor modifier gets
+     * position using the [TextFieldValue.selection] value which corresponds to the text directly,
+     * and therefore does not require the translation.
+     */
+    var layoutResult: TextLayoutResultProxy? = null
 
     /**
      * The gesture detector status, to indicate whether current status is selection or editing.
@@ -595,4 +603,110 @@
             placeholders = emptyList()
         )
     }
+}
+
+/**
+ * Request focus on tap. If already focused, makes sure the keyboard is requested.
+ */
+private fun tapToFocus(
+    state: TextFieldState,
+    focusRequester: FocusRequester,
+    textInputService: TextInputService?,
+    allowKeyboard: Boolean
+) {
+    if (!state.hasFocus) {
+        focusRequester.requestFocus()
+    } else if (allowKeyboard) {
+        textInputService?.showSoftwareKeyboard(state.inputSession)
+    }
+}
+
+@OptIn(InternalTextApi::class)
+private fun notifyTextInputServiceOnFocusChange(
+    textInputService: TextInputService,
+    state: TextFieldState,
+    value: TextFieldValue,
+    imeOptions: ImeOptions,
+    onValueChange: (TextFieldValue) -> Unit,
+    onImeActionPerformed: (ImeAction) -> Unit,
+    onTextInputStarted: (SoftwareKeyboardController) -> Unit,
+    offsetMapping: OffsetMapping
+) {
+    if (state.hasFocus) {
+        state.inputSession = TextFieldDelegate.onFocus(
+            textInputService,
+            value,
+            state.processor,
+            imeOptions,
+            onValueChange,
+            onImeActionPerformed
+        )
+        if (state.inputSession != NO_SESSION) {
+            onTextInputStarted(SoftwareKeyboardController(textInputService, state.inputSession))
+        }
+        state.layoutCoordinates?.let { coords ->
+            state.layoutResult?.let { layoutResult ->
+                TextFieldDelegate.notifyFocusedRect(
+                    value,
+                    state.textDelegate,
+                    layoutResult.value,
+                    coords,
+                    textInputService,
+                    state.inputSession,
+                    state.hasFocus,
+                    offsetMapping
+                )
+            }
+        }
+    } else {
+        TextFieldDelegate.onBlur(
+            textInputService,
+            state.inputSession,
+            state.processor,
+            false,
+            onValueChange
+        )
+    }
+}
+
+@Composable
+private fun SelectionToolbarAndHandles(manager: TextFieldSelectionManager, show: Boolean) {
+    if (show) {
+        with(manager) {
+            state?.layoutResult?.value?.let {
+                if (!value.selection.collapsed) {
+                    val startOffset = offsetMapping.originalToTransformed(value.selection.start)
+                    val endOffset = offsetMapping.originalToTransformed(value.selection.end)
+                    val startDirection = it.getBidiRunDirection(startOffset)
+                    val endDirection = it.getBidiRunDirection(max(endOffset - 1, 0))
+                    val directions = Pair(startDirection, endDirection)
+                    if (manager.state?.showSelectionHandleStart == true) {
+                        TextFieldSelectionHandle(
+                            isStartHandle = true,
+                            directions = directions,
+                            manager = manager
+                        )
+                    }
+                    if (manager.state?.showSelectionHandleEnd == true) {
+                        TextFieldSelectionHandle(
+                            isStartHandle = false,
+                            directions = directions,
+                            manager = manager
+                        )
+                    }
+                }
+
+                state?.let { textFieldState ->
+                    // If in selection mode (when the floating toolbar is shown) a new symbol
+                    // from the keyboard is entered, text field should enter the editing mode
+                    // instead.
+                    if (isTextChanged()) textFieldState.showFloatingToolbar = false
+                    if (textFieldState.hasFocus) {
+                        if (textFieldState.showFloatingToolbar) showSelectionToolbar()
+                        else hideSelectionToolbar()
+                    }
+                }
+            }
+        }
+    } else manager.hideSelectionToolbar()
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldCursor.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldCursor.kt
index 93c94c3..895a836 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldCursor.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldCursor.kt
@@ -35,6 +35,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.isSpecified
 import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.input.OffsetMapping
@@ -47,14 +48,15 @@
     state: TextFieldState,
     value: TextFieldValue,
     offsetMapping: OffsetMapping,
-    cursorColor: Color
-) = composed {
+    cursorColor: Color,
+    enabled: Boolean
+) = if (enabled) composed {
     // this should be a disposable clock, but it's not available in this module
     // however, we only launch one animation and guarantee that we stop it (via snap) in dispose
     val animationClocks = AmbientAnimationClock.current
     val cursorAlpha = remember(animationClocks) { AnimatedFloatModel(0f, animationClocks) }
 
-    if (state.hasFocus && value.selection.collapsed && cursorColor != Color.Unspecified) {
+    if (state.hasFocus && value.selection.collapsed && cursorColor.isSpecified) {
         onCommit(cursorColor, value.annotatedString) {
             if (@Suppress("DEPRECATION_ERROR") blinkingCursorEnabled) {
                 cursorAlpha.animateTo(0f, anim = cursorAnimationSpec)
@@ -71,7 +73,7 @@
             if (cursorAlphaValue != 0f) {
                 val transformedOffset = offsetMapping
                     .originalToTransformed(value.selection.start)
-                val cursorRect = state.layoutResult?.getCursorRect(transformedOffset)
+                val cursorRect = state.layoutResult?.value?.getCursorRect(transformedOffset)
                     ?: Rect(0f, 0f, 0f, 0f)
                 val cursorWidth = DefaultCursorThickness.toPx()
                 val cursorX = (cursorRect.left + cursorWidth / 2)
@@ -89,7 +91,7 @@
     } else {
         Modifier
     }
-}
+} else this
 
 @Stable
 private class AnimatedFloatModel(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
index 79f0ea9..e8036e0 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
@@ -214,7 +214,7 @@
          * Sets the cursor position. Should be called when TextField has focus.
          *
          * @param position The event position in composable coordinate.
-         * @param textLayoutResult The text layout result
+         * @param textLayoutResult The text layout result proxy
          * @param editProcessor The edit processor
          * @param offsetMapping The offset map
          * @param onValueChange The callback called when the new editor state arrives.
@@ -222,7 +222,7 @@
         @JvmStatic
         internal fun setCursorOffset(
             position: Offset,
-            textLayoutResult: TextLayoutResult,
+            textLayoutResult: TextLayoutResultProxy,
             editProcessor: EditProcessor,
             offsetMapping: OffsetMapping,
             onValueChange: (TextFieldValue) -> Unit
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDragGestureFilter.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDragGestureFilter.kt
index 560431f..94844bc 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDragGestureFilter.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDragGestureFilter.kt
@@ -35,7 +35,8 @@
     onPress: (Offset) -> Unit,
     onRelease: (Offset) -> Unit,
     interactionState: InteractionState?,
-): Modifier = composed {
+    enabled: Boolean = true
+): Modifier = if (enabled) composed {
     val tracker = remember { DragEventTracker() }
     // TODO(shepshapard): PressIndicator doesn't seem to be the right thing to use here.  It
     //  actually may be functionally correct, but might mostly suggest that it should not
@@ -66,7 +67,7 @@
                 }
             }
         )
-}
+} else this
 
 /**
  * Helper class for tracking dragging event.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldGestureModifiers.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldGestureModifiers.kt
new file mode 100644
index 0000000..b7abf97
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldGestureModifiers.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text
+
+import androidx.compose.foundation.InteractionState
+import androidx.compose.foundation.focusable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.gesture.DragObserver
+import androidx.compose.ui.gesture.LongPressDragObserver
+import androidx.compose.ui.gesture.dragGestureFilter
+import androidx.compose.ui.gesture.longPressDragGestureFilter
+import androidx.compose.ui.gesture.tapGestureFilter
+
+// Touch selection
+internal fun Modifier.longPressDragGestureFilter(
+    observer: LongPressDragObserver,
+    enabled: Boolean
+) = if (enabled) this.then(longPressDragGestureFilter(observer)) else this
+
+internal fun Modifier.focusRequestTapModifier(onTap: (Offset) -> Unit, enabled: Boolean) =
+    if (enabled) this.tapGestureFilter(onTap) else this
+
+// Focus modifiers
+internal fun Modifier.textFieldFocusModifier(
+    enabled: Boolean,
+    focusRequester: FocusRequester,
+    interactionState: InteractionState?,
+    onFocusChanged: (FocusState) -> Unit
+): Modifier {
+    if (!enabled) return this
+
+    return this
+        .focusRequester(focusRequester)
+        .onFocusChanged(onFocusChanged)
+        .focusable(interactionState = interactionState)
+}
+
+// Mouse
+internal fun Modifier.mouseDragGestureFilter(
+    dragObserver: DragObserver,
+    enabled: Boolean
+) = if (enabled) this.dragGestureFilter(dragObserver, startDragImmediately = true) else this
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldScroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldScroll.kt
index 10e7ee7..253c5f2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldScroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldScroll.kt
@@ -16,14 +16,18 @@
 
 package androidx.compose.foundation.text
 
+import androidx.compose.animation.asDisposableClock
 import androidx.compose.foundation.InteractionState
-import androidx.compose.foundation.gestures.rememberScrollableController
+import androidx.compose.foundation.animation.defaultFlingConfig
+import androidx.compose.foundation.gestures.ScrollableController
 import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.layout.offset
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.savedinstancestate.Saver
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.savedinstancestate.listSaver
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.structuralEqualityPolicy
 import androidx.compose.ui.Modifier
@@ -35,7 +39,7 @@
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.node.Ref
+import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.platform.AmbientLayoutDirection
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.text.TextLayoutResult
@@ -49,79 +53,96 @@
 import kotlin.math.min
 import kotlin.math.roundToInt
 
+// Scrollable
+internal fun Modifier.textFieldScrollable(
+    scrollerPosition: TextFieldScrollerPosition,
+    interactionState: InteractionState? = null,
+    enabled: Boolean = true
+) = composed(
+    inspectorInfo = debugInspectorInfo {
+        name = "textFieldScrollable"
+        properties["scrollerPosition"] = scrollerPosition
+        properties["interactionState"] = interactionState
+        properties["enabled"] = enabled
+    }
+) {
+    // do not reverse direction only in case of RTL in horizontal orientation
+    val rtl = AmbientLayoutDirection.current == LayoutDirection.Rtl
+    val reverseDirection = scrollerPosition.orientation == Orientation.Vertical || !rtl
+    val controller = rememberScrollableController(
+        scrollerPosition,
+        interactionState = interactionState
+    ) { delta ->
+        val newOffset = scrollerPosition.offset + delta
+        val consumedDelta = when {
+            newOffset > scrollerPosition.maximum ->
+                scrollerPosition.maximum - scrollerPosition.offset
+            newOffset < 0f -> -scrollerPosition.offset
+            else -> delta
+        }
+        scrollerPosition.offset += consumedDelta
+        consumedDelta
+    }
+    val scroll = Modifier.scrollable(
+        orientation = scrollerPosition.orientation,
+        canScroll = { scrollerPosition.maximum != 0f },
+        reverseDirection = reverseDirection,
+        controller = controller,
+        enabled = enabled
+    )
+    scroll
+}
+
+// Layout
 internal fun Modifier.textFieldScroll(
-    orientation: Orientation,
     scrollerPosition: TextFieldScrollerPosition,
     textFieldValue: TextFieldValue,
     visualTransformation: VisualTransformation,
-    interactionState: InteractionState,
-    textLayoutResult: Ref<TextLayoutResult?>,
-    enabled: Boolean = true
-) = composed(
-    factory = {
-        // do not reverse direction only in case of RTL in horizontal orientation
-        val rtl = AmbientLayoutDirection.current == LayoutDirection.Rtl
-        val reverseDirection = orientation == Orientation.Vertical || !rtl
-        val controller =
-            rememberScrollableController(interactionState = interactionState) { delta ->
-                val newOffset = scrollerPosition.offset + delta
-                val consumedDelta = when {
-                    newOffset > scrollerPosition.maximum ->
-                        scrollerPosition.maximum - scrollerPosition.offset
-                    newOffset < 0f -> -scrollerPosition.offset
-                    else -> delta
-                }
-                scrollerPosition.offset += consumedDelta
-                consumedDelta
-            }
-        val scroll = Modifier.scrollable(
-            orientation = orientation,
-            canScroll = { scrollerPosition.maximum != 0f },
-            reverseDirection = reverseDirection,
-            controller = controller,
-            enabled = enabled
-        )
+    textLayoutResultProvider: () -> TextLayoutResultProxy?
+): Modifier {
+    val orientation = scrollerPosition.orientation
+    val cursorOffset = scrollerPosition.getOffsetToFollow(textFieldValue.selection)
+    scrollerPosition.previousSelection = textFieldValue.selection
 
-        val cursorOffset = scrollerPosition.getOffsetToFollow(textFieldValue.selection)
-        scrollerPosition.previousSelection = textFieldValue.selection
+    val transformedText = visualTransformation.filter(textFieldValue.annotatedString)
 
-        val transformedText = visualTransformation.filter(textFieldValue.annotatedString)
-
-        val layout = when (orientation) {
-            Orientation.Vertical ->
-                VerticalScrollLayoutModifier(
-                    scrollerPosition,
-                    cursorOffset,
-                    transformedText,
-                    textLayoutResult
-                )
-            Orientation.Horizontal ->
-                HorizontalScrollLayoutModifier(
-                    scrollerPosition,
-                    cursorOffset,
-                    transformedText,
-                    textLayoutResult
-                )
-        }
-        this.then(scroll).clipToBounds().then(layout)
-    },
-    inspectorInfo = debugInspectorInfo {
-        name = "textFieldScroll"
-        properties["orientation"] = orientation
-        properties["scrollerPosition"] = scrollerPosition
-        properties["enabled"] = enabled
-        properties["textFieldValue"] = textFieldValue
-        properties["visualTransformation"] = visualTransformation
-        properties["interactionState"] = interactionState
-        properties["textLayoutResult"] = textLayoutResult
+    val layout = when (orientation) {
+        Orientation.Vertical ->
+            VerticalScrollLayoutModifier(
+                scrollerPosition,
+                cursorOffset,
+                transformedText,
+                textLayoutResultProvider
+            )
+        Orientation.Horizontal ->
+            HorizontalScrollLayoutModifier(
+                scrollerPosition,
+                cursorOffset,
+                transformedText,
+                textLayoutResultProvider
+            )
     }
-)
+    return this.clipToBounds().then(layout)
+}
+
+@Composable
+private fun rememberScrollableController(
+    vararg inputs: Any?,
+    interactionState: InteractionState? = null,
+    consumeScrollDelta: (Float) -> Float
+): ScrollableController {
+    val clocks = AmbientAnimationClock.current.asDisposableClock()
+    val flingConfig = defaultFlingConfig()
+    return remember(inputs, clocks, flingConfig, interactionState) {
+        ScrollableController(consumeScrollDelta, flingConfig, clocks, interactionState)
+    }
+}
 
 private data class VerticalScrollLayoutModifier(
     val scrollerPosition: TextFieldScrollerPosition,
     val cursorOffset: Int,
     val transformedText: TransformedText,
-    val textLayoutResult: Ref<TextLayoutResult?>
+    val textLayoutResultProvider: () -> TextLayoutResultProxy?
 ) : LayoutModifier {
     override fun MeasureScope.measure(
         measurable: Measurable,
@@ -135,7 +156,7 @@
             val cursorRect = getCursorRectInScroller(
                 cursorOffset = cursorOffset,
                 transformedText = transformedText,
-                textLayoutResult = textLayoutResult.value,
+                textLayoutResult = textLayoutResultProvider()?.value,
                 rtl = false,
                 textFieldWidth = placeable.width
             )
@@ -157,7 +178,7 @@
     val scrollerPosition: TextFieldScrollerPosition,
     val cursorOffset: Int,
     val transformedText: TransformedText,
-    val textLayoutResult: Ref<TextLayoutResult?>
+    val textLayoutResultProvider: () -> TextLayoutResultProxy?
 ) : LayoutModifier {
     override fun MeasureScope.measure(
         measurable: Measurable,
@@ -171,7 +192,7 @@
             val cursorRect = getCursorRectInScroller(
                 cursorOffset = cursorOffset,
                 transformedText = transformedText,
-                textLayoutResult = textLayoutResult.value,
+                textLayoutResult = textLayoutResultProvider()?.value,
                 rtl = layoutDirection == LayoutDirection.Rtl,
                 textFieldWidth = placeable.width
             )
@@ -216,7 +237,14 @@
 }
 
 @Stable
-internal class TextFieldScrollerPosition(initial: Float = 0f) {
+internal class TextFieldScrollerPosition(
+    initialOrientation: Orientation,
+    initial: Float = 0f,
+) {
+
+    /*@VisibleForTesting*/
+    constructor() : this(Orientation.Vertical)
+
     /**
      * Left or top offset. Takes values from 0 to [maximum].
      * Taken with the opposite sign defines the x or y position of the text field in the
@@ -243,6 +271,8 @@
      */
     var previousSelection: TextRange = TextRange.Zero
 
+    var orientation by mutableStateOf(initialOrientation, structuralEqualityPolicy())
+
     fun update(
         orientation: Orientation,
         cursorRect: Rect,
@@ -283,11 +313,14 @@
     }
 
     companion object {
-        val Saver = Saver<TextFieldScrollerPosition, Float>(
-            save = { it.offset },
+        val Saver = listSaver<TextFieldScrollerPosition, Any>(
+            save = {
+                listOf(it.offset, it.orientation == Orientation.Vertical)
+            },
             restore = { restored ->
                 TextFieldScrollerPosition(
-                    initial = restored
+                    if (restored[1] as Boolean) Orientation.Vertical else Orientation.Horizontal,
+                    restored[0] as Float
                 )
             }
         )
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLayoutResultProxy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLayoutResultProxy.kt
new file mode 100644
index 0000000..1778378
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLayoutResultProxy.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.TextLayoutResult
+
+internal class TextLayoutResultProxy(val value: TextLayoutResult) {
+    // TextLayoutResult methods
+    fun getOffsetForPosition(position: Offset): Int {
+        val shiftedOffset = shiftedOffset(position)
+        return value.getOffsetForPosition(shiftedOffset)
+    }
+
+    fun getLineForVerticalPosition(vertical: Float): Int {
+        val shiftedVertical = shiftedOffset(Offset(0f, vertical)).y
+        return value.getLineForVerticalPosition(shiftedVertical)
+    }
+
+    fun getLineEnd(lineIndex: Int, visibleEnd: Boolean = false): Int =
+        value.getLineEnd(lineIndex, visibleEnd)
+
+    /** Returns true if the screen coordinates position (x,y) corresponds to a character displayed
+     * in the view. Returns false when the position is in the empty space of left/right of text.
+     */
+    fun isPositionOnText(offset: Offset): Boolean {
+        val shiftedOffset = shiftedOffset(offset)
+        val line = value.getLineForVerticalPosition(shiftedOffset.y)
+        return shiftedOffset.x >= value.getLineLeft(line) &&
+            shiftedOffset.x <= value.getLineRight(line)
+    }
+
+    // Shift offset
+    /** Measured bounds of the decoration box and inner text field. Together used to
+     * calculate the relative touch offset. Because touches are applied on the decoration box, we
+     * need to translate it to the inner text field coordinates.
+     */
+    var innerTextFieldCoordinates: LayoutCoordinates? = null
+    var decorationBoxCoordinates: LayoutCoordinates? = null
+
+    private fun shiftedOffset(offset: Offset): Offset {
+        // If offset is outside visible bounds of the inner text field, use visible bounds edges
+        val visibleInnerTextFieldRect = innerTextFieldCoordinates?.let { inner ->
+            decorationBoxCoordinates?.localBoundingBoxOf(inner)
+        } ?: Rect.Zero
+        val coercedOffset = offset.coerceIn(visibleInnerTextFieldRect)
+
+        // Translates touch to the inner text field coordinates
+        return innerTextFieldCoordinates?.let { innerTextFieldCoordinates ->
+            decorationBoxCoordinates?.let { decorationBoxCoordinates ->
+                innerTextFieldCoordinates.localPositionOf(decorationBoxCoordinates, coercedOffset)
+            }
+        } ?: coercedOffset
+    }
+}
+
+private fun Offset.coerceIn(rect: Rect): Offset {
+    val xOffset = when {
+        x < rect.left -> rect.left
+        x > rect.right -> rect.right
+        else -> x
+    }
+    val yOffset = when {
+        y < rect.top -> rect.top
+        y > rect.bottom -> rect.bottom
+        else -> y
+    }
+    return Offset(xOffset, yOffset)
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
index 2bf656b..51ae7af 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
@@ -19,6 +19,9 @@
 import androidx.compose.foundation.text.TextFieldDelegate
 import androidx.compose.foundation.text.TextFieldState
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.geometry.Offset
@@ -96,6 +99,11 @@
     var focusRequester: FocusRequester? = null
 
     /**
+     * Defines if paste and cut toolbar menu actions should be shown
+     */
+    var editable by mutableStateOf(true)
+
+    /**
      * The beginning position of the drag gesture. Every time a new drag gesture starts, it wil be
      * recalculated.
      */
@@ -123,7 +131,7 @@
             }
 
             // Long Press at the blank area, the cursor should show up at the end of the line.
-            if (!isPositionOnText(pxPosition)) {
+            if (state?.layoutResult?.isPositionOnText(pxPosition) != true) {
                 state?.layoutResult?.let { layoutResult ->
                     val offset = offsetMapping.transformedToOriginal(
                         layoutResult.getLineEnd(
@@ -245,7 +253,7 @@
             override fun onDrag(dragDistance: Offset): Offset {
                 dragTotalDistance += dragDistance
 
-                state?.layoutResult?.let { layoutResult ->
+                state?.layoutResult?.value?.let { layoutResult ->
                     val startOffset = if (isStartHandle)
                         layoutResult.getOffsetForPosition(dragBeginPosition + dragTotalDistance)
                     else
@@ -305,7 +313,8 @@
         if (!value.selection.collapsed) {
             // if selection was not collapsed, set a default cursor location, otherwise
             // don't change the location of the cursor.
-            val newValue = value.copy(selection = TextRange.Zero)
+            val newCursorOffset = value.selection.max
+            val newValue = value.copy(selection = TextRange(newCursorOffset))
             onValueChange(newValue)
         }
         setSelectionStatus(false)
@@ -405,7 +414,7 @@
     internal fun getHandlePosition(isStartHandle: Boolean): Offset {
         val offset = if (isStartHandle) value.selection.start else value.selection.end
         return getSelectionHandleCoordinates(
-            textLayoutResult = state?.layoutResult!!,
+            textLayoutResult = state?.layoutResult!!.value,
             offset = offsetMapping.originalToTransformed(offset),
             isStart = isStartHandle,
             areHandlesCrossed = value.selection.reversed
@@ -448,8 +457,8 @@
         textToolbar?.showMenu(
             rect = getContentRect(),
             >
-            >
-            >
+             (editable) paste else null,
+             (editable) cut else null,
             >
         )
     }
@@ -483,7 +492,7 @@
                 state?.layoutCoordinates?.localToRoot(
                     Offset(
                         0f,
-                        it.layoutResult?.getCursorRect(
+                        it.layoutResult?.value?.getCursorRect(
                             value.selection.start.coerceIn(
                                 0,
                                 max(0, value.text.length - 1)
@@ -495,7 +504,7 @@
                 state?.layoutCoordinates?.localToRoot(
                     Offset(
                         0f,
-                        it.layoutResult?.getCursorRect(
+                        it.layoutResult?.value?.getCursorRect(
                             value.selection.end.coerceIn(
                                 0,
                                 max(0, value.text.length - 1)
@@ -529,7 +538,7 @@
         )
 
         val newTransformedSelection = getTextFieldSelection(
-            textLayoutResult = state?.layoutResult,
+            textLayoutResult = state?.layoutResult?.value,
             rawStartOffset = transformedStartOffset,
             rawEndOffset = transformedEndOffset,
             previousSelection = if (transformedSelection.collapsed) null else transformedSelection,
@@ -575,18 +584,6 @@
             selection = selection.constrain(0, annotatedString.length)
         )
     }
-
-    /** Returns true if the screen coordinates position (x,y) corresponds to a character displayed
-     * in the view. Returns false when the position is in the empty space of left/right of text.
-     */
-    private fun isPositionOnText(offset: Offset): Boolean {
-        state?.layoutResult?.let {
-            val line = it.getLineForVerticalPosition(offset.y)
-            if (offset.x < it.getLineLeft(line) || offset.x > it.getLineRight(line)) return false
-            return true
-        }
-        return false
-    }
 }
 
 @Composable
diff --git a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/text/selection/DesktopTextFieldSelectionManagerTest.kt b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/text/selection/DesktopTextFieldSelectionManagerTest.kt
index 3857e63..a02b698 100644
--- a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/text/selection/DesktopTextFieldSelectionManagerTest.kt
+++ b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/text/selection/DesktopTextFieldSelectionManagerTest.kt
@@ -74,8 +74,9 @@
 
         state.layoutResult = mock()
         state.textDelegate = mock()
+        whenever(state.layoutResult!!.value).thenReturn(mock())
         whenever(state.textDelegate.density).thenReturn(density)
-        whenever(state.layoutResult!!.layoutInput).thenReturn(
+        whenever(state.layoutResult!!.value.layoutInput).thenReturn(
             TextLayoutInput(
                 text = AnnotatedString(text),
                 style = TextStyle.Default,
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
index a93f802e..483dad7 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
@@ -72,6 +72,7 @@
     private lateinit var textInputService: TextInputService
     private lateinit var layoutCoordinates: LayoutCoordinates
     private lateinit var multiParagraphIntrinsics: MultiParagraphIntrinsics
+    private lateinit var textLayoutResultProxy: TextLayoutResultProxy
     private lateinit var textLayoutResult: TextLayoutResult
 
     /**
@@ -93,6 +94,8 @@
         layoutCoordinates = mock()
         multiParagraphIntrinsics = mock()
         textLayoutResult = mock()
+        textLayoutResultProxy = mock()
+        whenever(textLayoutResultProxy.value).thenReturn(textLayoutResult)
     }
 
     @Test
@@ -101,11 +104,11 @@
         val offset = 10
         val editorState = TextFieldValue(text = "Hello, World", selection = TextRange(1))
         whenever(processor.mBufferState).thenReturn(editorState)
-        whenever(textLayoutResult.getOffsetForPosition(position)).thenReturn(offset)
+        whenever(textLayoutResultProxy.getOffsetForPosition(position)).thenReturn(offset)
 
         TextFieldDelegate.setCursorOffset(
             position,
-            textLayoutResult,
+            textLayoutResultProxy,
             processor,
             OffsetMapping.Identity,
             onValueChange
@@ -295,11 +298,11 @@
         val offset = 10
         val editorState = TextFieldValue(text = "Hello, World", selection = TextRange(1))
         whenever(processor.mBufferState).thenReturn(editorState)
-        whenever(textLayoutResult.getOffsetForPosition(position)).thenReturn(offset)
+        whenever(textLayoutResultProxy.getOffsetForPosition(position)).thenReturn(offset)
 
         TextFieldDelegate.setCursorOffset(
             position,
-            textLayoutResult,
+            textLayoutResultProxy,
             processor,
             skippingOffsetMap,
             onValueChange
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
index d490f58..c42210e9 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
@@ -17,6 +17,7 @@
 package androidx.compose.foundation.text.selection
 
 import androidx.compose.foundation.text.TextFieldState
+import androidx.compose.foundation.text.TextLayoutResultProxy
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
@@ -28,6 +29,7 @@
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.TextLayoutInput
+import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.OffsetMapping
@@ -49,7 +51,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.ArgumentMatchers
 import org.mockito.invocation.InvocationOnMock
 import org.mockito.stubbing.Answer
 
@@ -62,7 +63,7 @@
     private var value = TextFieldValue(text)
     private val lambda: (TextFieldValue) -> Unit = { value = it }
     private val spyLambda = spy(lambda)
-    private val state = TextFieldState(mock())
+    private lateinit var state: TextFieldState
 
     private val dragBeginPosition = Offset.Zero
     private val dragDistance = Offset(300f, 15f)
@@ -70,7 +71,8 @@
     private val dragOffset = text.indexOf('r')
     private val fakeTextRange = TextRange(0, "Hello".length)
     private val dragTextRange = TextRange("Hello".length + 1, text.length)
-
+    private val layoutResult: TextLayoutResult = mock()
+    private val layoutResultProxy: TextLayoutResultProxy = mock()
     private val manager = TextFieldSelectionManager()
 
     private val clipboardManager = mock<ClipboardManager>()
@@ -82,17 +84,13 @@
     fun setup() {
         manager.offsetMapping = offsetMapping
         manager.>
-        manager.state = state
         manager.value = value
         manager.clipboardManager = clipboardManager
         manager.textToolbar = textToolbar
         manager.hapticFeedBack = hapticFeedback
         manager.focusRequester = focusRequester
 
-        state.layoutResult = mock()
-        state.textDelegate = mock()
-        whenever(state.textDelegate.density).thenReturn(density)
-        whenever(state.layoutResult!!.layoutInput).thenReturn(
+        whenever(layoutResult.layoutInput).thenReturn(
             TextLayoutInput(
                 text = AnnotatedString(text),
                 style = TextStyle.Default,
@@ -106,21 +104,27 @@
                 constraints = Constraints()
             )
         )
-        whenever(state.layoutResult!!.getOffsetForPosition(dragBeginPosition)).thenReturn(
-            beginOffset
-        )
-        whenever(state.layoutResult!!.getOffsetForPosition(dragDistance)).thenReturn(dragOffset)
 
-        whenever(
-            state.layoutResult!!.getWordBoundary(ArgumentMatchers.intThat { it in 0 until 5 })
-        ).thenAnswer(TextRangeAnswer(fakeTextRange))
-        whenever(
-            state.layoutResult!!.getWordBoundary(ArgumentMatchers.intThat { it in 6 until 11 })
-        ).thenAnswer(TextRangeAnswer(dragTextRange))
-
-        whenever(state.layoutResult!!.getBidiRunDirection(any()))
+        whenever(layoutResult.getWordBoundary(beginOffset))
+            .thenAnswer(TextRangeAnswer(fakeTextRange))
+        whenever(layoutResult.getWordBoundary(dragOffset))
+            .thenAnswer(TextRangeAnswer(dragTextRange))
+        whenever(layoutResult.getBidiRunDirection(any()))
             .thenReturn(ResolvedTextDirection.Ltr)
-        whenever(state.layoutResult!!.getBoundingBox(any())).thenReturn(Rect.Zero)
+        whenever(layoutResult.getBoundingBox(any())).thenReturn(Rect.Zero)
+        // left or right handle drag
+        whenever(layoutResult.getOffsetForPosition(dragBeginPosition)).thenReturn(beginOffset)
+        whenever(layoutResult.getOffsetForPosition(dragDistance)).thenReturn(dragOffset)
+        // touch drag
+        whenever(layoutResultProxy.getOffsetForPosition(dragBeginPosition)).thenReturn(beginOffset)
+        whenever(layoutResultProxy.getOffsetForPosition(dragDistance)).thenReturn(dragOffset)
+
+        whenever(layoutResultProxy.value).thenReturn(layoutResult)
+
+        state = TextFieldState(mock())
+        state.layoutResult = layoutResultProxy
+        manager.state = state
+        whenever(state.textDelegate.density).thenReturn(density)
     }
 
     @Test
@@ -133,6 +137,7 @@
 
     @Test
     fun TextFieldSelectionManager_touchSelectionObserver_onLongPress() {
+        whenever(layoutResultProxy.isPositionOnText(dragBeginPosition)).thenReturn(true)
 
         manager.touchSelectionObserver.onLongPress(dragBeginPosition)
 
@@ -155,11 +160,12 @@
         // Setup
         val fakeLineNumber = 0
         val fakeLineEnd = text.length
-        whenever(state.layoutResult!!.getLineForVerticalPosition(dragBeginPosition.y))
+        whenever(layoutResultProxy.isPositionOnText(dragBeginPosition)).thenReturn(false)
+        whenever(layoutResultProxy.getLineForVerticalPosition(dragBeginPosition.y))
             .thenReturn(fakeLineNumber)
-        whenever(state.layoutResult!!.getLineLeft(fakeLineNumber))
+        whenever(layoutResult.getLineLeft(fakeLineNumber))
             .thenReturn(dragBeginPosition.x + 1.0f)
-        whenever(state.layoutResult!!.getLineEnd(fakeLineNumber)).thenReturn(fakeLineEnd)
+        whenever(layoutResultProxy.getLineEnd(fakeLineNumber)).thenReturn(fakeLineEnd)
 
         // Act
         manager.touchSelectionObserver.onLongPress(dragBeginPosition)
@@ -281,7 +287,7 @@
         manager.deselect()
 
         verify(textToolbar, times(1)).hide()
-        assertThat(value.selection).isEqualTo(TextRange.Zero)
+        assertThat(value.selection).isEqualTo(TextRange("Hello".length))
         assertThat(state.selectionIsOn).isFalse()
     }
 
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/WithConstraintsBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/WithConstraintsBenchmark.kt
index 7328504..981b708 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/WithConstraintsBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/WithConstraintsBenchmark.kt
@@ -52,17 +52,17 @@
 
     @Test
     fun no_withconstraints_inner_recompose() {
-        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout { NoWithConstraintsTestCase() }
+        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout({ NoWithConstraintsTestCase() })
     }
 
     @Test
     fun withconstraints_inner_recompose() {
-        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout { WithConstraintsTestCase() }
+        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout({ WithConstraintsTestCase() })
     }
 
     @Test
     fun withconstraints_changing_constraints() {
-        benchmarkRule.toggleStateBenchmarkMeasureLayout { ChangingConstraintsTestCase() }
+        benchmarkRule.toggleStateBenchmarkMeasureLayout({ ChangingConstraintsTestCase() })
     }
 }
 
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt
index 379fa20..2d90b70 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt
@@ -54,7 +54,10 @@
 
     @Test
     fun first_compose() {
-        benchmarkRule.benchmarkFirstCompose(checkboxCaseFactory)
+        benchmarkRule.benchmarkFirstCompose(
+            checkboxCaseFactory,
+            assertNoPendingRecompositions = false
+        )
     }
 
     @Test
@@ -74,22 +77,31 @@
 
     @Test
     fun toggleCheckbox_recompose() {
-        benchmarkRule.toggleStateBenchmarkRecompose(checkboxCaseFactory)
+        benchmarkRule.toggleStateBenchmarkRecompose(
+            checkboxCaseFactory,
+            assertOneRecomposition = false
+        )
     }
 
     @Test
     fun toggleCheckbox_measure() {
-        benchmarkRule.toggleStateBenchmarkMeasure(checkboxCaseFactory)
+        benchmarkRule.toggleStateBenchmarkMeasure(
+            checkboxCaseFactory,
+            assertOneRecomposition = false
+        )
     }
 
     @Test
     fun toggleCheckbox_layout() {
-        benchmarkRule.toggleStateBenchmarkLayout(checkboxCaseFactory)
+        benchmarkRule.toggleStateBenchmarkLayout(
+            checkboxCaseFactory,
+            assertOneRecomposition = false
+        )
     }
 
     @Test
     fun toggleCheckbox_draw() {
-        benchmarkRule.toggleStateBenchmarkDraw(checkboxCaseFactory)
+        benchmarkRule.toggleStateBenchmarkDraw(checkboxCaseFactory, assertOneRecomposition = false)
     }
 
     @Test
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RadioGroupBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RadioGroupBenchmark.kt
index d704a6f..c62622f 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RadioGroupBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RadioGroupBenchmark.kt
@@ -57,7 +57,7 @@
 
     @Test
     fun first_compose() {
-        benchmarkRule.benchmarkFirstCompose(radioCaseFactory)
+        benchmarkRule.benchmarkFirstCompose(radioCaseFactory, assertNoPendingRecompositions = false)
     }
 
     @Test
@@ -77,22 +77,25 @@
 
     @Test
     fun toggleRadio_recompose() {
-        benchmarkRule.toggleStateBenchmarkRecompose(radioCaseFactory)
+        benchmarkRule.toggleStateBenchmarkRecompose(
+            radioCaseFactory,
+            assertOneRecomposition = false
+        )
     }
 
     @Test
     fun toggleRadio_measure() {
-        benchmarkRule.toggleStateBenchmarkMeasure(radioCaseFactory)
+        benchmarkRule.toggleStateBenchmarkMeasure(radioCaseFactory, assertOneRecomposition = false)
     }
 
     @Test
     fun toggleRadio_layout() {
-        benchmarkRule.toggleStateBenchmarkLayout(radioCaseFactory)
+        benchmarkRule.toggleStateBenchmarkLayout(radioCaseFactory, assertOneRecomposition = false)
     }
 
     @Test
     fun toggleRadio_draw() {
-        benchmarkRule.toggleStateBenchmarkDraw(radioCaseFactory)
+        benchmarkRule.toggleStateBenchmarkDraw(radioCaseFactory, assertOneRecomposition = false)
     }
 }
 
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/TrailingLambdaBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/TrailingLambdaBenchmark.kt
index aea9e5a..0c5451b 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/TrailingLambdaBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/TrailingLambdaBenchmark.kt
@@ -44,22 +44,22 @@
 
     @Test
     fun withTrailingLambdas_compose() {
-        benchmarkRule.benchmarkFirstCompose { WithTrailingLambdas() }
+        benchmarkRule.benchmarkFirstCompose({ WithTrailingLambdas() })
     }
 
     @Test
     fun withTrailingLambdas_recompose() {
-        benchmarkRule.toggleStateBenchmarkRecompose { WithTrailingLambdas() }
+        benchmarkRule.toggleStateBenchmarkRecompose({ WithTrailingLambdas() })
     }
 
     @Test
     fun withoutTrailingLambdas_compose() {
-        benchmarkRule.benchmarkFirstCompose { WithoutTrailingLambdas() }
+        benchmarkRule.benchmarkFirstCompose({ WithoutTrailingLambdas() })
     }
 
     @Test
     fun withoutTrailingLambdas_recompose() {
-        benchmarkRule.toggleStateBenchmarkRecompose { WithoutTrailingLambdas() }
+        benchmarkRule.toggleStateBenchmarkRecompose({ WithoutTrailingLambdas() })
     }
 }
 
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmark.kt
index bfe8f59..fb531d0 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmark.kt
@@ -41,7 +41,7 @@
 
     @Test
     fun xml_compose() {
-        benchmarkRule.benchmarkFirstCompose { XmlVectorTestCase() }
+        benchmarkRule.benchmarkFirstCompose({ XmlVectorTestCase() })
     }
 
     @Test
@@ -61,7 +61,7 @@
 
     @Test
     fun programmatic_compose() {
-        benchmarkRule.benchmarkFirstCompose { ProgrammaticVectorTestCase() }
+        benchmarkRule.benchmarkFirstCompose({ ProgrammaticVectorTestCase() })
     }
 
     @Test
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
index 3c14b88..f84c765 100644
--- a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
+++ b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
@@ -182,21 +182,21 @@
             LayoutDirection.Rtl -> Icons.Filled.ArrowForward
         }
         IconButton( {
-            Icon(icon)
+            Icon(icon, null)
         }
     }
 
     @Composable
     fun Filter(onClick: () -> Unit) {
         IconButton(modifier = Modifier.testTag(Tags.FilterButton),  {
-            Icon(Icons.Filled.Search)
+            Icon(Icons.Filled.Search, null)
         }
     }
 
     @Composable
     fun Settings(onClick: () -> Unit) {
         IconButton( {
-            Icon(Icons.Filled.Settings)
+            Icon(Icons.Filled.Settings, null)
         }
     }
 }
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoFilter.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoFilter.kt
index 47e8e9c..b01334f 100644
--- a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoFilter.kt
+++ b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoFilter.kt
@@ -88,7 +88,7 @@
         }
         TopAppBar(backgroundColor = appBarColor, contentColor = onSurface) {
             IconButton(modifier = Modifier.align(Alignment.CenterVertically),  {
-                Icon(Icons.Filled.Close)
+                Icon(Icons.Filled.Close, null)
             }
             FilterField(
                 filterText,
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
index 6e2bb73..656fb76 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
@@ -210,6 +210,7 @@
 
     Icon(
         imageVector = vectorResource(R.drawable.ic_plane),
+        contentDescription = stringResource(R.string.plane_description),
         tint = colorResource(R.color.Blue700)
     )
 }
@@ -421,15 +422,16 @@
     }
     object string {
         const val ok = 4
+        const val plane_description = 5
     }
     object dimen {
-        const val padding_small = 5
+        const val padding_small = 6
     }
     object drawable {
-        const val ic_plane = 6
+        const val ic_plane = 7
     }
     object color {
-        const val Blue700 = 7
+        const val Blue700 = 8
     }
 }
 
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/navigation/Navigation.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/navigation/Navigation.kt
index f11321f..18ac649 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/navigation/Navigation.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/navigation/Navigation.kt
@@ -56,11 +56,13 @@
  * No action required if it's modified.
  */
 
-@Composable private fun NavigationSnippet1() {
+@Composable
+private fun NavigationSnippet1() {
     val navController = rememberNavController()
 }
 
-@Composable private fun NavigationSnippet2(navController: NavHostController) {
+@Composable
+private fun NavigationSnippet2(navController: NavHostController) {
     NavHost(navController = navController, startDestination = "profile") {
         composable("profile") { Profile(/*...*/) }
         composable("friendslist") { FriendsList(/*...*/) }
@@ -69,7 +71,8 @@
 }
 
 private object NavigationSnippet3 {
-    @Composable fun Profile(navController: NavController) {
+    @Composable
+    fun Profile(navController: NavController) {
         /*...*/
         Button( navController.navigate("friends") }) {
             Text(text = "Navigate next")
@@ -78,14 +81,16 @@
     }
 }
 
-@Composable private fun NavigationSnippet4(navController: NavHostController) {
+@Composable
+private fun NavigationSnippet4(navController: NavHostController) {
     NavHost(navController = navController, startDestination = "profile/{userId}") {
         /*...*/
         composable("profile/{userId}") { /*...*/ }
     }
 }
 
-@Composable private fun NavigationSnippet5(navController: NavHostController) {
+@Composable
+private fun NavigationSnippet5(navController: NavHostController) {
     NavHost(navController = navController, startDestination = "profile/{userId}") {
         /*...*/
         composable(
@@ -95,17 +100,20 @@
     }
 }
 
-@Composable private fun NavGraphBuilder.NavigationSnippet6(navController: NavHostController) {
+@Composable
+private fun NavGraphBuilder.NavigationSnippet6(navController: NavHostController) {
     composable("profile/{userId}") { backStackEntry ->
         Profile(navController, backStackEntry.arguments?.getString("userId"))
     }
 }
 
-@Composable private fun NavigationSnippet7(navController: NavHostController) {
+@Composable
+private fun NavigationSnippet7(navController: NavHostController) {
     navController.navigate("profile/user1234")
 }
 
-@Composable private fun NavGraphBuilder.NavigationSnippet8(navController: NavHostController) {
+@Composable
+private fun NavGraphBuilder.NavigationSnippet8(navController: NavHostController) {
     composable(
         "profile?userId={userId}",
         arguments = listOf(navArgument("userId") { defaultValue = "me" })
@@ -116,7 +124,8 @@
 
 /* Deep links */
 
-@Composable private fun NavGraphBuilder.NavigationSnippet9(navController: NavHostController) {
+@Composable
+private fun NavGraphBuilder.NavigationSnippet9(navController: NavHostController) {
     val uri = "https://example.com"
 
     composable(
@@ -127,7 +136,8 @@
     }
 }
 
-@Composable private fun NavigationSnippet10() {
+@Composable
+private fun NavigationSnippet10() {
     val id = "exampleId"
     val context = AmbientContext.current
     val deepLinkIntent = Intent(
@@ -143,7 +153,8 @@
     }
 }
 
-@Composable private fun NavigationSnippet11(items: List<Screen>) {
+@Composable
+private fun NavigationSnippet11(items: List<Screen>) {
     val navController = rememberNavController()
     Scaffold(
         bottomBar = {
@@ -152,7 +163,7 @@
                 val currentRoute = navBackStackEntry?.arguments?.getString(KEY_ROUTE)
                 items.forEach { screen ->
                     BottomNavigationItem(
-                        icon = { Icon(Icons.Filled.Favorite) },
+                        icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
                         label = { Text(stringResource(screen.resourceId)) },
                         selected = currentRoute == screen.route,
                         >
@@ -178,7 +189,8 @@
     }
 }
 
-@Composable private fun NavGraphBuilder.NavigationSnippet12(navController: NavHostController) {
+@Composable
+private fun NavGraphBuilder.NavigationSnippet12(navController: NavHostController) {
     composable(
         "profile?userId={userId}",
         arguments = listOf(navArgument("userId") { defaultValue = "me" })
@@ -200,12 +212,31 @@
     }
 }
 
-@Composable private fun Profile() { }
-@Composable private fun Profile(userId: String?, content: @Composable (String) -> Unit) { }
-@Composable private fun Profile(navController: NavHostController) { }
-@Composable private fun FriendsList() { }
-@Composable private fun FriendsList(navController: NavHostController) { }
-@Composable private fun Profile(navController: NavHostController, arg: String?) { TODO() }
+@Composable
+private fun Profile() {
+}
+
+@Composable
+private fun Profile(userId: String?, content: @Composable (String) -> Unit) {
+}
+
+@Composable
+private fun Profile(navController: NavHostController) {
+}
+
+@Composable
+private fun FriendsList() {
+}
+
+@Composable
+private fun FriendsList(navController: NavHostController) {
+}
+
+@Composable
+private fun Profile(navController: NavHostController, arg: String?) {
+    TODO()
+}
+
 private class MyActivity
 private sealed class Screen(val route: String, @StringRes val resourceId: Int) {
     object Profile : Screen("profile", R.string.profile)
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
index dd6f636..3d70e48 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
@@ -47,6 +47,7 @@
 import androidx.compose.runtime.savedinstancestate.savedInstanceState
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.viewModel
 import androidx.lifecycle.LiveData
@@ -235,12 +236,18 @@
                     Text(text = body, Modifier.padding(top = 8.dp))
                     // change expanded in response to click events
                     IconButton( expanded = false }, modifier = Modifier.fillMaxWidth()) {
-                        Icon(Icons.Default.ExpandLess)
+                        Icon(
+                            Icons.Default.ExpandLess,
+                            contentDescription = stringResource(R.string.expand_less)
+                        )
                     }
                 } else {
                     // change expanded in response to click events
                     IconButton( expanded = true }, modifier = Modifier.fillMaxWidth()) {
-                        Icon(Icons.Default.ExpandMore)
+                        Icon(
+                            Icons.Default.ExpandMore,
+                            contentDescription = stringResource(R.string.expand_more)
+                        )
                     }
                 }
             }
@@ -284,12 +291,18 @@
                 if (expanded) {
                     Spacer(Modifier.height(8.dp))
                     Text(body)
-                    IconButton( Modifier.fillMaxWidth()) {
-                        Icon(Icons.Default.ExpandLess)
+                    IconButton( modifier = Modifier.fillMaxWidth()) {
+                        Icon(
+                            Icons.Default.ExpandLess,
+                            contentDescription = stringResource(R.string.expand_less)
+                        )
                     }
                 } else {
-                    IconButton( Modifier.fillMaxWidth()) {
-                        Icon(Icons.Default.ExpandMore)
+                    IconButton( modifier = Modifier.fillMaxWidth()) {
+                        Icon(
+                            Icons.Default.ExpandMore,
+                            contentDescription = stringResource(R.string.expand_more)
+                        )
                     }
                 }
             }
@@ -325,6 +338,13 @@
     }
 }
 
+private object R {
+    object string {
+        const val expand_less = 0
+        const val expand_more = 1
+    }
+}
+
 private const val it = 1
 private lateinit var helloViewModel: StateSnippet2.HelloViewModel
 private fun computeTextFormatting(st: String): String = ""
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/tutorial/Tutorial.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/tutorial/Tutorial.kt
index 18a6b75..8cf64b3 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/tutorial/Tutorial.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/tutorial/Tutorial.kt
@@ -159,7 +159,7 @@
         Column(
             modifier = Modifier.padding(16.dp)
         ) {
-            Image(image)
+            Image(image, contentDescription = null)
 
             Text("A day in Shark Fin Cove")
             Text("Davenport, California")
@@ -179,9 +179,12 @@
                 .preferredHeight(180.dp)
                 .fillMaxWidth()
 
-            Image(image,
+            Image(
+                image,
+                contentDescription = null,
                 modifier = imageModifier,
-                contentScale = ContentScale.Crop)
+                contentScale = ContentScale.Crop
+            )
 
             Text("A day in Shark Fin Cove")
             Text("Davenport, California")
@@ -205,9 +208,12 @@
                 .fillMaxWidth()
                 .clip(shape = RoundedCornerShape(4.dp))
 
-            Image(image,
+            Image(
+                image,
+                contentDescription = null,
                 modifier = imageModifier,
-                contentScale = ContentScale.Crop)
+                contentScale = ContentScale.Crop
+            )
             Spacer(Modifier.preferredHeight(16.dp))
 
             Text("A day in Shark Fin Cove")
@@ -230,9 +236,12 @@
                     .fillMaxWidth()
                     .clip(shape = RoundedCornerShape(4.dp))
 
-                Image(image,
+                Image(
+                    image,
+                    contentDescription = null,
                     modifier = imageModifier,
-                    contentScale = ContentScale.Crop)
+                    contentScale = ContentScale.Crop
+                )
                 Spacer(Modifier.preferredHeight(16.dp))
 
                 Text("A day in Shark Fin Cove")
@@ -257,9 +266,12 @@
                     .fillMaxWidth()
                     .clip(shape = RoundedCornerShape(4.dp))
 
-                Image(image,
+                Image(
+                    image,
+                    contentDescription = null,
                     modifier = imageModifier,
-                    contentScale = ContentScale.Crop)
+                    contentScale = ContentScale.Crop
+                )
                 Spacer(Modifier.preferredHeight(16.dp))
 
                 Text("A day in Shark Fin Cove",
@@ -287,9 +299,12 @@
                     .fillMaxWidth()
                     .clip(shape = RoundedCornerShape(4.dp))
 
-                Image(image,
+                Image(
+                    image,
+                    contentDescription = null,
                     modifier = imageModifier,
-                    contentScale = ContentScale.Crop)
+                    contentScale = ContentScale.Crop
+                )
                 Spacer(Modifier.preferredHeight(16.dp))
 
                 Text(
@@ -321,9 +336,11 @@
                     .fillMaxWidth()
                     .clip(shape = RoundedCornerShape(4.dp))
 
-                Image(image,
+                Image(
+                    image, null,
                     modifier = imageModifier,
-                    contentScale = ContentScale.Crop)
+                    contentScale = ContentScale.Crop
+                )
                 Spacer(Modifier.preferredHeight(16.dp))
 
                 Text(
diff --git a/compose/integration-tests/src/androidTest/java/androidx/ui/integration/test/ImageVectorTest.kt b/compose/integration-tests/src/androidTest/java/androidx/ui/integration/test/ImageVectorTest.kt
index 102d854..3e0c8b2 100644
--- a/compose/integration-tests/src/androidTest/java/androidx/ui/integration/test/ImageVectorTest.kt
+++ b/compose/integration-tests/src/androidTest/java/androidx/ui/integration/test/ImageVectorTest.kt
@@ -96,7 +96,7 @@
             }
             val imageVector =
                 vectorResource(R.drawable.ic_pathfill_sample)
-            Image(imageVector, modifier = Modifier.testTag(testTag))
+            Image(imageVector, null, modifier = Modifier.testTag(testTag))
         }
 
         rule.onNodeWithTag(testTag).captureToImage().asAndroidBitmap().apply {
diff --git a/compose/material/material-icons-core/samples/src/main/java/androidx/compose/material/icons/samples/IconSamples.kt b/compose/material/material-icons-core/samples/src/main/java/androidx/compose/material/icons/samples/IconSamples.kt
index 24bd565..36b37ac 100644
--- a/compose/material/material-icons-core/samples/src/main/java/androidx/compose/material/icons/samples/IconSamples.kt
+++ b/compose/material/material-icons-core/samples/src/main/java/androidx/compose/material/icons/samples/IconSamples.kt
@@ -38,7 +38,7 @@
 @Sampled
 @Composable
 fun DrawIcon() {
-    Icon(Icons.Rounded.Menu)
+    Icon(Icons.Rounded.Menu, contentDescription = "Localized description")
 }
 
 @Composable
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index 190a613..68ce800 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -127,8 +127,8 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ButtonColors {
-    method public long backgroundColor-0d7_KjU(boolean enabled);
-    method public long contentColor-0d7_KjU(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled);
   }
 
   public final class ButtonDefaults {
@@ -157,7 +157,7 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ButtonElevation {
-    method public float elevation-D9Ej5fM(boolean enabled, androidx.compose.foundation.InteractionState interactionState);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> elevation(boolean enabled, androidx.compose.foundation.InteractionState interactionState);
   }
 
   public final class ButtonKt {
@@ -171,9 +171,9 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface CheckboxColors {
-    method public long borderColor-0d7_KjU(boolean enabled, androidx.compose.ui.state.ToggleableState state);
-    method public long boxColor-0d7_KjU(boolean enabled, androidx.compose.ui.state.ToggleableState state);
-    method public long checkmarkColor-0d7_KjU(androidx.compose.ui.state.ToggleableState state);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> borderColor(boolean enabled, androidx.compose.ui.state.ToggleableState state);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> boxColor(boolean enabled, androidx.compose.ui.state.ToggleableState state);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> checkmarkColor(androidx.compose.ui.state.ToggleableState state);
   }
 
   public final class CheckboxDefaults {
@@ -313,7 +313,7 @@
   }
 
   public final class ElevationKt {
-    method public static void animateElevation-K5VTZl0(androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,?>, optional androidx.compose.foundation.Interaction? from, optional androidx.compose.foundation.Interaction? to, float target);
+    method public static suspend Object? animateElevation-_EXKJ8o(androidx.compose.animation.core.Animatable<androidx.compose.ui.unit.Dp,?>, optional androidx.compose.foundation.Interaction? from, optional androidx.compose.foundation.Interaction? to, float target, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.unit.Dp> getAmbientAbsoluteElevation();
   }
 
@@ -344,7 +344,7 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface FloatingActionButtonElevation {
-    method public float elevation-D9Ej5fM(androidx.compose.foundation.InteractionState interactionState);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> elevation(androidx.compose.foundation.InteractionState interactionState);
   }
 
   public final class FloatingActionButtonKt {
@@ -364,9 +364,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-tu7MLKI(androidx.compose.ui.graphics.vector.ImageVector imageVector, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon-8NTYWNk(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon-BG621w0(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon-hGAziDE(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
   public final class ListItemKt {
@@ -449,7 +449,7 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface RadioButtonColors {
-    method public long radioColor-0d7_KjU(boolean enabled, boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> radioColor(boolean enabled, boolean selected);
   }
 
   public final class RadioButtonDefaults {
@@ -514,7 +514,7 @@
     method @androidx.compose.runtime.Composable public static void Slider-B7rb6FQ(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit> onValueChangeEnd, optional androidx.compose.foundation.InteractionState interactionState, optional long thumbColor, optional long activeTrackColor, optional long inactiveTrackColor, optional long activeTickColor, optional long inactiveTickColor);
   }
 
-  @androidx.compose.material.ExperimentalMaterialApi public interface SnackbarData {
+  public interface SnackbarData {
     method public void dismiss();
     method public String? getActionLabel();
     method public androidx.compose.material.SnackbarDuration getDuration();
@@ -540,10 +540,10 @@
   }
 
   public final class SnackbarHostKt {
-    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void SnackbarHost(androidx.compose.material.SnackbarHostState hostState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarData,kotlin.Unit> snackbar);
+    method @androidx.compose.runtime.Composable public static void SnackbarHost(androidx.compose.material.SnackbarHostState hostState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarData,kotlin.Unit> snackbar);
   }
 
-  @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class SnackbarHostState {
+  @androidx.compose.runtime.Stable public final class SnackbarHostState {
     ctor public SnackbarHostState();
     method public androidx.compose.material.SnackbarData? getCurrentSnackbarData();
     method public suspend Object? showSnackbar(String message, optional String? actionLabel, optional androidx.compose.material.SnackbarDuration duration, optional kotlin.coroutines.Continuation<? super androidx.compose.material.SnackbarResult> p);
@@ -628,8 +628,8 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface SwitchColors {
-    method public long thumbColor-0d7_KjU(boolean enabled, boolean checked);
-    method public long trackColor-0d7_KjU(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> thumbColor(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trackColor(boolean enabled, boolean checked);
   }
 
   public final class SwitchDefaults {
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index 190a613..68ce800 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -127,8 +127,8 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ButtonColors {
-    method public long backgroundColor-0d7_KjU(boolean enabled);
-    method public long contentColor-0d7_KjU(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled);
   }
 
   public final class ButtonDefaults {
@@ -157,7 +157,7 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ButtonElevation {
-    method public float elevation-D9Ej5fM(boolean enabled, androidx.compose.foundation.InteractionState interactionState);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> elevation(boolean enabled, androidx.compose.foundation.InteractionState interactionState);
   }
 
   public final class ButtonKt {
@@ -171,9 +171,9 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface CheckboxColors {
-    method public long borderColor-0d7_KjU(boolean enabled, androidx.compose.ui.state.ToggleableState state);
-    method public long boxColor-0d7_KjU(boolean enabled, androidx.compose.ui.state.ToggleableState state);
-    method public long checkmarkColor-0d7_KjU(androidx.compose.ui.state.ToggleableState state);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> borderColor(boolean enabled, androidx.compose.ui.state.ToggleableState state);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> boxColor(boolean enabled, androidx.compose.ui.state.ToggleableState state);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> checkmarkColor(androidx.compose.ui.state.ToggleableState state);
   }
 
   public final class CheckboxDefaults {
@@ -313,7 +313,7 @@
   }
 
   public final class ElevationKt {
-    method public static void animateElevation-K5VTZl0(androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,?>, optional androidx.compose.foundation.Interaction? from, optional androidx.compose.foundation.Interaction? to, float target);
+    method public static suspend Object? animateElevation-_EXKJ8o(androidx.compose.animation.core.Animatable<androidx.compose.ui.unit.Dp,?>, optional androidx.compose.foundation.Interaction? from, optional androidx.compose.foundation.Interaction? to, float target, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.unit.Dp> getAmbientAbsoluteElevation();
   }
 
@@ -344,7 +344,7 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface FloatingActionButtonElevation {
-    method public float elevation-D9Ej5fM(androidx.compose.foundation.InteractionState interactionState);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> elevation(androidx.compose.foundation.InteractionState interactionState);
   }
 
   public final class FloatingActionButtonKt {
@@ -364,9 +364,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-tu7MLKI(androidx.compose.ui.graphics.vector.ImageVector imageVector, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon-8NTYWNk(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon-BG621w0(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon-hGAziDE(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
   public final class ListItemKt {
@@ -449,7 +449,7 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface RadioButtonColors {
-    method public long radioColor-0d7_KjU(boolean enabled, boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> radioColor(boolean enabled, boolean selected);
   }
 
   public final class RadioButtonDefaults {
@@ -514,7 +514,7 @@
     method @androidx.compose.runtime.Composable public static void Slider-B7rb6FQ(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit> onValueChangeEnd, optional androidx.compose.foundation.InteractionState interactionState, optional long thumbColor, optional long activeTrackColor, optional long inactiveTrackColor, optional long activeTickColor, optional long inactiveTickColor);
   }
 
-  @androidx.compose.material.ExperimentalMaterialApi public interface SnackbarData {
+  public interface SnackbarData {
     method public void dismiss();
     method public String? getActionLabel();
     method public androidx.compose.material.SnackbarDuration getDuration();
@@ -540,10 +540,10 @@
   }
 
   public final class SnackbarHostKt {
-    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void SnackbarHost(androidx.compose.material.SnackbarHostState hostState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarData,kotlin.Unit> snackbar);
+    method @androidx.compose.runtime.Composable public static void SnackbarHost(androidx.compose.material.SnackbarHostState hostState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarData,kotlin.Unit> snackbar);
   }
 
-  @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class SnackbarHostState {
+  @androidx.compose.runtime.Stable public final class SnackbarHostState {
     ctor public SnackbarHostState();
     method public androidx.compose.material.SnackbarData? getCurrentSnackbarData();
     method public suspend Object? showSnackbar(String message, optional String? actionLabel, optional androidx.compose.material.SnackbarDuration duration, optional kotlin.coroutines.Continuation<? super androidx.compose.material.SnackbarResult> p);
@@ -628,8 +628,8 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface SwitchColors {
-    method public long thumbColor-0d7_KjU(boolean enabled, boolean checked);
-    method public long trackColor-0d7_KjU(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> thumbColor(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trackColor(boolean enabled, boolean checked);
   }
 
   public final class SwitchDefaults {
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index 190a613..68ce800 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -127,8 +127,8 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ButtonColors {
-    method public long backgroundColor-0d7_KjU(boolean enabled);
-    method public long contentColor-0d7_KjU(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled);
   }
 
   public final class ButtonDefaults {
@@ -157,7 +157,7 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ButtonElevation {
-    method public float elevation-D9Ej5fM(boolean enabled, androidx.compose.foundation.InteractionState interactionState);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> elevation(boolean enabled, androidx.compose.foundation.InteractionState interactionState);
   }
 
   public final class ButtonKt {
@@ -171,9 +171,9 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface CheckboxColors {
-    method public long borderColor-0d7_KjU(boolean enabled, androidx.compose.ui.state.ToggleableState state);
-    method public long boxColor-0d7_KjU(boolean enabled, androidx.compose.ui.state.ToggleableState state);
-    method public long checkmarkColor-0d7_KjU(androidx.compose.ui.state.ToggleableState state);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> borderColor(boolean enabled, androidx.compose.ui.state.ToggleableState state);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> boxColor(boolean enabled, androidx.compose.ui.state.ToggleableState state);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> checkmarkColor(androidx.compose.ui.state.ToggleableState state);
   }
 
   public final class CheckboxDefaults {
@@ -313,7 +313,7 @@
   }
 
   public final class ElevationKt {
-    method public static void animateElevation-K5VTZl0(androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,?>, optional androidx.compose.foundation.Interaction? from, optional androidx.compose.foundation.Interaction? to, float target);
+    method public static suspend Object? animateElevation-_EXKJ8o(androidx.compose.animation.core.Animatable<androidx.compose.ui.unit.Dp,?>, optional androidx.compose.foundation.Interaction? from, optional androidx.compose.foundation.Interaction? to, float target, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public static androidx.compose.runtime.ProvidableAmbient<androidx.compose.ui.unit.Dp> getAmbientAbsoluteElevation();
   }
 
@@ -344,7 +344,7 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface FloatingActionButtonElevation {
-    method public float elevation-D9Ej5fM(androidx.compose.foundation.InteractionState interactionState);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> elevation(androidx.compose.foundation.InteractionState interactionState);
   }
 
   public final class FloatingActionButtonKt {
@@ -364,9 +364,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-tu7MLKI(androidx.compose.ui.graphics.vector.ImageVector imageVector, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon-8NTYWNk(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon-BG621w0(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon-hGAziDE(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
   }
 
   public final class ListItemKt {
@@ -449,7 +449,7 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface RadioButtonColors {
-    method public long radioColor-0d7_KjU(boolean enabled, boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> radioColor(boolean enabled, boolean selected);
   }
 
   public final class RadioButtonDefaults {
@@ -514,7 +514,7 @@
     method @androidx.compose.runtime.Composable public static void Slider-B7rb6FQ(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit> onValueChangeEnd, optional androidx.compose.foundation.InteractionState interactionState, optional long thumbColor, optional long activeTrackColor, optional long inactiveTrackColor, optional long activeTickColor, optional long inactiveTickColor);
   }
 
-  @androidx.compose.material.ExperimentalMaterialApi public interface SnackbarData {
+  public interface SnackbarData {
     method public void dismiss();
     method public String? getActionLabel();
     method public androidx.compose.material.SnackbarDuration getDuration();
@@ -540,10 +540,10 @@
   }
 
   public final class SnackbarHostKt {
-    method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void SnackbarHost(androidx.compose.material.SnackbarHostState hostState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarData,kotlin.Unit> snackbar);
+    method @androidx.compose.runtime.Composable public static void SnackbarHost(androidx.compose.material.SnackbarHostState hostState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarData,kotlin.Unit> snackbar);
   }
 
-  @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class SnackbarHostState {
+  @androidx.compose.runtime.Stable public final class SnackbarHostState {
     ctor public SnackbarHostState();
     method public androidx.compose.material.SnackbarData? getCurrentSnackbarData();
     method public suspend Object? showSnackbar(String message, optional String? actionLabel, optional androidx.compose.material.SnackbarDuration duration, optional kotlin.coroutines.Continuation<? super androidx.compose.material.SnackbarResult> p);
@@ -628,8 +628,8 @@
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface SwitchColors {
-    method public long thumbColor-0d7_KjU(boolean enabled, boolean checked);
-    method public long trackColor-0d7_KjU(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> thumbColor(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trackColor(boolean enabled, boolean checked);
   }
 
   public final class SwitchDefaults {
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ButtonDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ButtonDemo.kt
index 170084f..388b6c2 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ButtonDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ButtonDemo.kt
@@ -171,7 +171,7 @@
 
     IconToggleButton(checked = checked, enabled = false,  checked = it }) {
         val tint by animateAsState(if (checked) Color(0xFFEC407A) else Color(0xFFB0BEC5))
-        Icon(Icons.Filled.Favorite, tint = tint)
+        Icon(Icons.Filled.Favorite, contentDescription = "Favorite", tint = tint)
     }
 }
 
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 babd9ed..8c7a242 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
@@ -115,7 +115,7 @@
         )
 
         Box(Modifier.fillMaxSize()) {
-            Image(modifier = inputModifier, bitmap = colorWheel.image)
+            Image(modifier = inputModifier, contentDescription = null, bitmap = colorWheel.image)
             val color = colorWheel.colorForPosition(position)
             if (color.isSpecified) {
                 Magnifier(visible = isDragging, position = position, color = color)
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
index fd2842e..dc48864 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
@@ -30,7 +30,6 @@
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.widthIn
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.selection.selectable
@@ -175,8 +174,12 @@
                                 "Label" + if (selectedOption == Option.Error) "*" else ""
                             Text(text = label)
                         },
-                        leadingIcon = { if (leadingChecked) Icon(Icons.Filled.Favorite) },
-                        trailingIcon = { if (trailingChecked) Icon(Icons.Filled.Info) },
+                        leadingIcon = {
+                            if (leadingChecked) Icon(Icons.Filled.Favorite, "Favorite")
+                        },
+                        trailingIcon = {
+                            if (trailingChecked) Icon(Icons.Filled.Info, "Info")
+                        },
                         isErrorValue = selectedOption == Option.Error,
                         modifier = Modifier.width(300.dp)
                     )
@@ -192,10 +195,14 @@
                                 "Label" + if (selectedOption == Option.Error) "*" else ""
                             Text(text = label)
                         },
-                        leadingIcon = { if (leadingChecked) Icon(Icons.Filled.Favorite) },
-                        trailingIcon = { if (trailingChecked) Icon(Icons.Filled.Info) },
+                        leadingIcon = {
+                            if (leadingChecked) Icon(Icons.Filled.Favorite, "Favorite")
+                        },
+                        trailingIcon = {
+                            if (trailingChecked) Icon(Icons.Filled.Info, "Info")
+                        },
                         isErrorValue = selectedOption == Option.Error,
-                        modifier = Modifier.widthIn(max = 300.dp)
+                        modifier = Modifier.width(300.dp)
                     )
             }
         }
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 009a3b3e..eee99c4 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
@@ -78,7 +78,7 @@
 
     val iconButton = @Composable {
         IconButton( expanded = true }) {
-            Icon(Icons.Default.MoreVert)
+            Icon(Icons.Default.MoreVert, null)
         }
     }
     DropdownMenu(
diff --git a/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/CommonUi.kt b/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/CommonUi.kt
index a2ee1ad..423fcc4 100644
--- a/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/CommonUi.kt
+++ b/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/CommonUi.kt
@@ -100,8 +100,9 @@
         Spacer(Modifier.preferredWidth(16.dp))
         Icon(
             Icons.Filled.ArrowForwardIos,
-            tint = Color.White.copy(alpha = 0.6f),
+            contentDescription = null,
             modifier = Modifier.preferredSize(12.dp).align(Alignment.CenterVertically),
+            tint = Color.White.copy(alpha = 0.6f)
         )
     }
     RallyDivider()
diff --git a/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/OverviewScreen.kt b/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/OverviewScreen.kt
index 9827abd..8773f93 100644
--- a/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/OverviewScreen.kt
+++ b/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/OverviewScreen.kt
@@ -33,6 +33,7 @@
 import androidx.compose.material.Text
 import androidx.compose.material.TextButton
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.studies.R
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -40,6 +41,7 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import java.util.Locale
 
@@ -126,7 +128,7 @@
             >
             modifier = Modifier.align(Alignment.Top)
         ) {
-            Icon(Icons.Filled.Sort)
+            Icon(Icons.Filled.Sort, contentDescription = stringResource(R.string.sort))
         }
     }
 }
diff --git a/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/RallyScreenState.kt b/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/RallyScreenState.kt
index 044c093..ae6e831 100644
--- a/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/RallyScreenState.kt
+++ b/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/RallyScreenState.kt
@@ -17,14 +17,26 @@
 package androidx.compose.material.studies.rally
 
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.studies.R
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.vector.ImageVector
 
 enum class RallyScreenState(
-    val icon: ImageVector,
+    val icon: ScreenIcon,
     val body: @Composable () -> Unit
 ) {
-    Overview(Icons.Filled.PieChart, { OverviewBody() }),
-    Accounts(Icons.Filled.AttachMoney, { AccountsBody(UserData.accounts) }),
-    Bills(Icons.Filled.MoneyOff, { BillsBody(UserData.bills) })
-}
\ No newline at end of file
+    Overview(
+        ScreenIcon(Icons.Filled.PieChart, contentDescription = R.string.overview),
+        @Composable { OverviewBody() }
+    ),
+    Accounts(
+        ScreenIcon(Icons.Filled.AttachMoney, contentDescription = R.string.account),
+        @Composable { AccountsBody(UserData.accounts) }
+    ),
+    Bills(
+        ScreenIcon(Icons.Filled.MoneyOff, contentDescription = R.string.bills),
+        @Composable { BillsBody(UserData.bills) }
+    )
+}
+
+class ScreenIcon(val icon: ImageVector, val contentDescription: Int)
\ No newline at end of file
diff --git a/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/TopAppBar.kt b/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/TopAppBar.kt
index 843d7d8..b49e23f 100644
--- a/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/TopAppBar.kt
+++ b/compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/TopAppBar.kt
@@ -38,7 +38,7 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import java.util.Locale
 
@@ -65,7 +65,7 @@
 @Composable
 private fun RallyTab(
     text: String,
-    icon: ImageVector,
+    icon: ScreenIcon,
     onSelected: () -> Unit,
     selected: Boolean
 ) {
@@ -81,7 +81,11 @@
                     indication = rememberRipple(bounded = false)
                 )
         ) {
-            Icon(icon, tint = tabTintColor)
+            Icon(
+                imageVector = icon.icon,
+                contentDescription = stringResource(icon.contentDescription),
+                tint = tabTintColor
+            )
             if (selected) {
                 Spacer(Modifier.preferredWidth(12.dp))
                 Text(text, color = tabTintColor)
diff --git a/compose/material/material/integration-tests/material-studies/src/main/res/values/strings.xml b/compose/material/material/integration-tests/material-studies/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7df5dd7b
--- /dev/null
+++ b/compose/material/material/integration-tests/material-studies/src/main/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <string name="sort">Sort</string>
+    <string name="overview">Account overview</string>
+    <string name="account">Accounts</string>
+    <string name="bills">Bills</string>
+</resources>
\ No newline at end of file
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/AppBarSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/AppBarSamples.kt
index 1019531..2d17900 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/AppBarSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/AppBarSamples.kt
@@ -36,16 +36,16 @@
         title = { Text("Simple TopAppBar") },
         navigationIcon = {
             IconButton( /* doSomething() */ }) {
-                Icon(Icons.Filled.Menu)
+                Icon(Icons.Filled.Menu, contentDescription = null)
             }
         },
         actions = {
             // RowScope here, so these icons will be placed horizontally
             IconButton( /* doSomething() */ }) {
-                Icon(Icons.Filled.Favorite)
+                Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
             }
             IconButton( /* doSomething() */ }) {
-                Icon(Icons.Filled.Favorite)
+                Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
             }
         }
     )
@@ -56,15 +56,15 @@
 fun SimpleBottomAppBar() {
     BottomAppBar {
         IconButton( /* doSomething() */ }) {
-            Icon(Icons.Filled.Menu)
+            Icon(Icons.Filled.Menu, contentDescription = "Localized description")
         }
         // The actions should be at the end of the BottomAppBar
         Spacer(Modifier.weight(1f, true))
         IconButton( /* doSomething() */ }) {
-            Icon(Icons.Filled.Favorite)
+            Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
         }
         IconButton( /* doSomething() */ }) {
-            Icon(Icons.Filled.Favorite)
+            Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
         }
     }
 }
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt
index 368287d..6895b34 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BackdropScaffoldSamples.kt
@@ -58,11 +58,11 @@
                 navigationIcon = {
                     if (scaffoldState.isConcealed) {
                         IconButton( scaffoldState.reveal() }) {
-                            Icon(Icons.Default.Menu)
+                            Icon(Icons.Default.Menu, contentDescription = "Localized description")
                         }
                     } else {
                         IconButton( scaffoldState.conceal() }) {
-                            Icon(Icons.Default.Close)
+                            Icon(Icons.Default.Close, contentDescription = "Localized description")
                         }
                     }
                 },
@@ -77,7 +77,7 @@
                             }
                         }
                     ) {
-                        Icon(Icons.Default.Favorite)
+                        Icon(Icons.Default.Favorite, contentDescription = "Localized description")
                     }
                 },
                 elevation = 0.dp,
@@ -103,7 +103,12 @@
                 items(50) {
                     ListItem(
                         text = { Text("Item $it") },
-                        icon = { Icon(Icons.Default.Favorite) }
+                        icon = {
+                            Icon(
+                                Icons.Default.Favorite,
+                                contentDescription = "Localized description"
+                            )
+                        }
                     )
                 }
             }
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BottomNavigationSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BottomNavigationSamples.kt
index 321d4bc..b51b529 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BottomNavigationSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BottomNavigationSamples.kt
@@ -38,7 +38,7 @@
     BottomNavigation {
         items.forEachIndexed { index, item ->
             BottomNavigationItem(
-                icon = { Icon(Icons.Filled.Favorite) },
+                icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
                 label = { Text(item) },
                 selected = selectedItem == index,
                  selectedItem = index }
@@ -55,7 +55,7 @@
     BottomNavigation {
         items.forEachIndexed { index, item ->
             BottomNavigationItem(
-                icon = { Icon(Icons.Filled.Favorite) },
+                icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
                 label = { Text(item) },
                 selected = selectedItem == index,
                  selectedItem = index },
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BottomSheetScaffoldSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BottomSheetScaffoldSamples.kt
index 8ef6b56..3a39938 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BottomSheetScaffoldSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/BottomSheetScaffoldSamples.kt
@@ -89,7 +89,7 @@
                 title = { Text("Bottom sheet scaffold") },
                 navigationIcon = {
                     IconButton( scaffoldState.drawerState.open() }) {
-                        Icon(Icons.Default.Menu)
+                        Icon(Icons.Default.Menu, contentDescription = "Localized description")
                     }
                 }
             )
@@ -104,7 +104,7 @@
                     }
                 }
             ) {
-                Icon(Icons.Default.Favorite)
+                Icon(Icons.Default.Favorite, contentDescription = "Localized description")
             }
         },
         floatingActionButtonPosition = FabPosition.End,
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ButtonSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ButtonSamples.kt
index 12cfe41..3539c00 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ButtonSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ButtonSamples.kt
@@ -58,7 +58,11 @@
 @Composable
 fun ButtonWithIconSample() {
     Button( /* Do something! */ }) {
-        Icon(Icons.Filled.Favorite, Modifier.size(ButtonDefaults.IconSize))
+        Icon(
+            Icons.Filled.Favorite,
+            contentDescription = null,
+            modifier = Modifier.size(ButtonDefaults.IconSize)
+        )
         Spacer(Modifier.size(ButtonDefaults.IconSpacing))
         Text("Like")
     }
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/FloatingActionButtonSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/FloatingActionButtonSamples.kt
index a90c6a5..da257ea 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/FloatingActionButtonSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/FloatingActionButtonSamples.kt
@@ -31,7 +31,7 @@
 @Composable
 fun SimpleFab() {
     FloatingActionButton( /*do something*/ }) {
-        Icon(Icons.Filled.Favorite)
+        Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
     }
 }
 
@@ -47,7 +47,7 @@
 @Composable
 fun SimpleExtendedFabWithIcon() {
     ExtendedFloatingActionButton(
-        icon = { Icon(Icons.Filled.Favorite) },
+        icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
         text = { Text("ADD TO BASKET") },
          /*do something*/ }
     )
@@ -57,7 +57,7 @@
 @Composable
 fun FluidExtendedFab() {
     ExtendedFloatingActionButton(
-        icon = { Icon(Icons.Filled.Favorite) },
+        icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
         text = { Text("FLUID FAB") },
          /*do something*/ },
         modifier = Modifier.fillMaxWidth()
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/IconButtonSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/IconButtonSamples.kt
index 84ac071..dde741b 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/IconButtonSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/IconButtonSamples.kt
@@ -34,7 +34,7 @@
 @Composable
 fun IconButtonSample() {
     IconButton( /* doSomething() */ }) {
-        Icon(Icons.Filled.Favorite)
+        Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
     }
 }
 
@@ -45,6 +45,6 @@
 
     IconToggleButton(checked = checked,  checked = it }) {
         val tint by animateAsState(if (checked) Color(0xFFEC407A) else Color(0xFFB0BEC5))
-        Icon(Icons.Filled.Favorite, tint = tint)
+        Icon(Icons.Filled.Favorite, contentDescription = "Localized description", tint = tint)
     }
 }
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 88f67fd..c41ca94 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
@@ -104,17 +104,35 @@
         Divider()
         ListItem(
             text = { Text("One line list item with 24x24 icon") },
-            icon = { Image(icon24x24, colorFilter = ColorFilter.tint(AmbientContentColor.current)) }
+            icon = {
+                Image(
+                    icon24x24,
+                    contentDescription = null,
+                    colorFilter = ColorFilter.tint(AmbientContentColor.current)
+                )
+            }
         )
         Divider()
         ListItem(
             text = { Text("One line list item with 40x40 icon") },
-            icon = { Image(icon40x40, colorFilter = ColorFilter.tint(AmbientContentColor.current)) }
+            icon = {
+                Image(
+                    icon40x40,
+                    contentDescription = null,
+                    colorFilter = ColorFilter.tint(AmbientContentColor.current)
+                )
+            }
         )
         Divider()
         ListItem(
             text = { Text("One line list item with 56x56 icon") },
-            icon = { Image(icon56x56, colorFilter = ColorFilter.tint(AmbientContentColor.current)) }
+            icon = {
+                Image(
+                    icon56x56,
+                    contentDescription = null,
+                    colorFilter = ColorFilter.tint(AmbientContentColor.current)
+                )
+            }
         )
         Divider()
         ListItem(
@@ -122,6 +140,7 @@
             icon = {
                 Image(
                     icon56x56,
+                    contentDescription = null,
                     colorFilter = ColorFilter.tint(AmbientContentColor.current)
                 )
             },
@@ -130,7 +149,7 @@
         Divider()
         ListItem(
             text = { Text("One line list item with trailing icon") },
-            trailing = { Icon(vectorIcon) }
+            trailing = { Icon(vectorIcon, contentDescription = "Localized description") }
         )
         Divider()
         ListItem(
@@ -138,10 +157,11 @@
             icon = {
                 Image(
                     icon40x40,
+                    contentDescription = null,
                     colorFilter = ColorFilter.tint(AmbientContentColor.current)
                 )
             },
-            trailing = { Icon(vectorIcon) }
+            trailing = { Icon(vectorIcon, contentDescription = "Localized description") }
         )
         Divider()
     }
@@ -165,13 +185,25 @@
         ListItem(
             text = { Text("Two line list item with 24x24 icon") },
             secondaryText = { Text("Secondary text") },
-            icon = { Image(icon24x24, colorFilter = ColorFilter.tint(AmbientContentColor.current)) }
+            icon = {
+                Image(
+                    icon24x24,
+                    contentDescription = null,
+                    colorFilter = ColorFilter.tint(AmbientContentColor.current)
+                )
+            }
         )
         Divider()
         ListItem(
             text = { Text("Two line list item with 40x40 icon") },
             secondaryText = { Text("Secondary text") },
-            icon = { Image(icon40x40, colorFilter = ColorFilter.tint(AmbientContentColor.current)) }
+            icon = {
+                Image(
+                    icon40x40,
+                    contentDescription = null,
+                    colorFilter = ColorFilter.tint(AmbientContentColor.current)
+                )
+            }
         )
         Divider()
         ListItem(
@@ -181,6 +213,7 @@
             icon = {
                 Image(
                     icon40x40,
+                    contentDescription = null,
                     colorFilter = ColorFilter.tint(AmbientContentColor.current)
                 )
             }
@@ -220,7 +253,13 @@
                 )
             },
             singleLineSecondaryText = false,
-            icon = { Image(icon24x24, colorFilter = ColorFilter.tint(AmbientContentColor.current)) }
+            icon = {
+                Image(
+                    icon24x24,
+                    contentDescription = null,
+                    colorFilter = ColorFilter.tint(AmbientContentColor.current)
+                )
+            }
         )
         Divider()
         ListItem(
@@ -232,7 +271,7 @@
                 )
             },
             singleLineSecondaryText = false,
-            trailing = { Icon(vectorIcon) }
+            trailing = { Icon(vectorIcon, "Localized description") }
         )
         Divider()
         ListItem(
@@ -254,12 +293,24 @@
         Divider()
         ListItem(
             text = { Text("פריט ברשימה אחת עם תמונה.") },
-            icon = { Image(icon40x40, colorFilter = ColorFilter.tint(AmbientContentColor.current)) }
+            icon = {
+                Image(
+                    icon40x40,
+                    contentDescription = null,
+                    colorFilter = ColorFilter.tint(AmbientContentColor.current)
+                )
+            }
         )
         Divider()
         ListItem(
             text = { Text("One line list item with 24x24 icon") },
-            icon = { Image(icon40x40, colorFilter = ColorFilter.tint(AmbientContentColor.current)) }
+            icon = {
+                Image(
+                    icon40x40,
+                    contentDescription = null,
+                    colorFilter = ColorFilter.tint(AmbientContentColor.current)
+                )
+            }
         )
         Divider()
         ListItem(
@@ -267,6 +318,7 @@
             trailing = {
                 Image(
                     icon24x24,
+                    contentDescription = null,
                     colorFilter = ColorFilter.tint(AmbientContentColor.current)
                 )
             }
@@ -300,6 +352,7 @@
             icon = {
                 Image(
                     icon40x40,
+                    contentDescription = null,
                     colorFilter = ColorFilter.tint(AmbientContentColor.current)
                 )
             }
@@ -311,6 +364,7 @@
             icon = {
                 Image(
                     icon40x40,
+                    contentDescription = null,
                     colorFilter = ColorFilter.tint(AmbientContentColor.current)
                 )
             },
@@ -345,7 +399,13 @@
             text = { Text("ثلاثة عناصر قائمة مع رمز") },
             overlineText = { Text("فوق الخط") },
             secondaryText = { Text("نص ثانوي") },
-            icon = { Image(icon40x40, colorFilter = ColorFilter.tint(AmbientContentColor.current)) }
+            icon = {
+                Image(
+                    icon40x40,
+                    contentDescription = null,
+                    colorFilter = ColorFilter.tint(AmbientContentColor.current)
+                )
+            }
         )
         Divider()
     }
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/MenuSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/MenuSamples.kt
index d423bd0..433bfbc 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/MenuSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/MenuSamples.kt
@@ -42,7 +42,7 @@
 
     val iconButton = @Composable {
         IconButton( expanded = true }) {
-            Icon(Icons.Default.MoreVert)
+            Icon(Icons.Default.MoreVert, contentDescription = "Localized description")
         }
     }
     DropdownMenu(
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ModalBottomSheetSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ModalBottomSheetSamples.kt
index 9c63c0e..40ce474 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ModalBottomSheetSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ModalBottomSheetSamples.kt
@@ -50,7 +50,12 @@
                 items(50) {
                     ListItem(
                         text = { Text("Item $it") },
-                        icon = { Icon(Icons.Default.Favorite) }
+                        icon = {
+                            Icon(
+                                Icons.Default.Favorite,
+                                contentDescription = "Localized description"
+                            )
+                        }
                     )
                 }
             }
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ScaffoldSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ScaffoldSamples.kt
index 8279439..1b95b94 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ScaffoldSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ScaffoldSamples.kt
@@ -32,7 +32,6 @@
 import androidx.compose.foundation.shape.CutCornerShape
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.BottomAppBar
-import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.material.ExtendedFloatingActionButton
 import androidx.compose.material.FabPosition
 import androidx.compose.material.Icon
@@ -92,7 +91,7 @@
                             scaffoldState.drawerState.open()
                         }
                     ) {
-                        Icon(Icons.Filled.Menu)
+                        Icon(Icons.Filled.Menu, contentDescription = "Localized description")
                     }
                 }
             )
@@ -162,7 +161,7 @@
                         scaffoldState.drawerState.open()
                     }
                 ) {
-                    Icon(Icons.Filled.Menu)
+                    Icon(Icons.Filled.Menu, contentDescription = "Localized description")
                 }
             }
         },
@@ -192,7 +191,6 @@
 
 @Sampled
 @Composable
-@OptIn(ExperimentalMaterialApi::class)
 fun ScaffoldWithSimpleSnackbar() {
     val scaffoldState = rememberScaffoldState()
     val scope = rememberCoroutineScope()
@@ -220,7 +218,6 @@
 }
 
 @Sampled
-@OptIn(ExperimentalMaterialApi::class)
 @Composable
 fun ScaffoldWithCustomSnackbar() {
     val scaffoldState = rememberScaffoldState()
@@ -258,7 +255,7 @@
 }
 
 @Sampled
-@OptIn(ExperimentalMaterialApi::class, ExperimentalCoroutinesApi::class, FlowPreview::class)
+@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
 @Composable
 fun ScaffoldWithCoroutinesSnackbar() {
     // decouple snackbar host state from scaffold state for demo purposes
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeToDismissSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeToDismissSamples.kt
index 4a896c2..4e651f3 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeToDismissSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SwipeToDismissSamples.kt
@@ -121,7 +121,11 @@
                         Modifier.fillMaxSize().background(color).padding(horizontal = 20.dp),
                         contentAlignment = alignment
                     ) {
-                        Icon(icon, Modifier.scale(scale))
+                        Icon(
+                            icon,
+                            contentDescription = "Localized description",
+                            modifier = Modifier.scale(scale)
+                        )
                     }
                 },
                 dismissContent = {
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TabSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TabSamples.kt
index af55358..a61e589 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TabSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TabSamples.kt
@@ -87,7 +87,7 @@
         TabRow(selectedTabIndex = state) {
             icons.forEachIndexed { index, icon ->
                 Tab(
-                    icon = { Icon(icon) },
+                    icon = { Icon(icon, contentDescription = "Favorite") },
                     selected = state == index,
                      state = index }
                 )
@@ -114,7 +114,7 @@
             titlesAndIcons.forEachIndexed { index, (title, icon) ->
                 Tab(
                     text = { Text(title) },
-                    icon = { Icon(icon) },
+                    icon = { Icon(icon, contentDescription = null) },
                     selected = state == index,
                      state = index }
                 )
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
index 83553c1..f631a97 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
@@ -75,8 +75,8 @@
         value = text,
          text = it },
         placeholder = { Text("placeholder") },
-        leadingIcon = { Icon(Icons.Filled.Favorite) },
-        trailingIcon = { Icon(Icons.Filled.Info) }
+        leadingIcon = { Icon(Icons.Filled.Favorite, contentDescription = "Localized description") },
+        trailingIcon = { Icon(Icons.Filled.Info, contentDescription = "Localized description") }
     )
 }
 
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AppBarTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AppBarTest.kt
index 05b8283..39baeaf 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AppBarTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AppBarTest.kt
@@ -198,7 +198,7 @@
      */
     private val FakeIcon = @Composable { modifier: Modifier ->
         IconButton( modifier = modifier) {
-            Icon(ColorPainter(Color.Red))
+            Icon(ColorPainter(Color.Red), null)
         }
     }
 
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationScreenshotTest.kt
index b32d261..c347707 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationScreenshotTest.kt
@@ -316,18 +316,18 @@
     Box(Modifier.semantics(mergeDescendants = true) {}.testTag(Tag)) {
         BottomNavigation {
             BottomNavigationItem(
-                icon = { Icon(Icons.Filled.Favorite) },
+                icon = { Icon(Icons.Filled.Favorite, null) },
                 selected = true,
                 >
                 interactionState = interactionState
             )
             BottomNavigationItem(
-                icon = { Icon(Icons.Filled.Favorite) },
+                icon = { Icon(Icons.Filled.Favorite, null) },
                 selected = false,
                 >
             )
             BottomNavigationItem(
-                icon = { Icon(Icons.Filled.Favorite) },
+                icon = { Icon(Icons.Filled.Favorite, null) },
                 selected = false,
                 >
             )
@@ -359,7 +359,7 @@
     Box(Modifier.semantics(mergeDescendants = true) {}.testTag(Tag)) {
         BottomNavigation(backgroundColor = backgroundColor) {
             BottomNavigationItem(
-                icon = { Icon(Icons.Filled.Favorite) },
+                icon = { Icon(Icons.Filled.Favorite, null) },
                 selected = true,
                 >
                 interactionState = interactionState,
@@ -367,14 +367,14 @@
                 unselectedContentColor = unselectedContentColor
             )
             BottomNavigationItem(
-                icon = { Icon(Icons.Filled.Favorite) },
+                icon = { Icon(Icons.Filled.Favorite, null) },
                 selected = false,
                 >
                 selectedContentColor = selectedContentColor,
                 unselectedContentColor = unselectedContentColor
             )
             BottomNavigationItem(
-                icon = { Icon(Icons.Filled.Favorite) },
+                icon = { Icon(Icons.Filled.Favorite, null) },
                 selected = false,
                 >
                 selectedContentColor = selectedContentColor,
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationTest.kt
index 107384f..dcdef28 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationTest.kt
@@ -81,7 +81,7 @@
                 BottomNavigation {
                     repeat(4) { index ->
                         BottomNavigationItem(
-                            icon = { Icon(Icons.Filled.Favorite) },
+                            icon = { Icon(Icons.Filled.Favorite, null) },
                             label = { Text("Item $index") },
                             selected = index == 0,
                             >
@@ -119,7 +119,7 @@
                     BottomNavigationItem(
                         modifier = Modifier.testTag("item"),
                         icon = {
-                            Icon(Icons.Filled.Favorite, Modifier.testTag("icon"))
+                            Icon(Icons.Filled.Favorite, null, Modifier.testTag("icon"))
                         },
                         label = {
                             Text("ItemText")
@@ -164,7 +164,7 @@
                     BottomNavigationItem(
                         modifier = Modifier.testTag("item"),
                         icon = {
-                            Icon(Icons.Filled.Favorite, Modifier.testTag("icon"))
+                            Icon(Icons.Filled.Favorite, null, Modifier.testTag("icon"))
                         },
                         label = {
                             Text("ItemText")
@@ -198,7 +198,7 @@
                     BottomNavigationItem(
                         modifier = Modifier.testTag("item"),
                         icon = {
-                            Icon(Icons.Filled.Favorite, Modifier.testTag("icon"))
+                            Icon(Icons.Filled.Favorite, null, Modifier.testTag("icon"))
                         },
                         label = {},
                         selected = false,
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt
index 7349c2a..9cc7ffc 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt
@@ -327,7 +327,7 @@
                             }.testTag(fabTag),
                         >
                     ) {
-                        Icon(Icons.Filled.Favorite)
+                        Icon(Icons.Filled.Favorite, null)
                     }
                 },
                 bodyContent = { Text("Content") }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/FloatingActionButtonTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/FloatingActionButtonTest.kt
index 10d8101..c00a8b0 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/FloatingActionButtonTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/FloatingActionButtonTest.kt
@@ -65,7 +65,7 @@
         rule.setMaterialContent {
             Box {
                 FloatingActionButton(modifier = Modifier.testTag("myButton"),  {
-                    Icon(Icons.Filled.Favorite)
+                    Icon(Icons.Filled.Favorite, null)
                 }
             }
         }
@@ -99,7 +99,7 @@
         rule
             .setMaterialContentForSizeAssertions {
                 FloatingActionButton( {
-                    Icon(Icons.Filled.Favorite)
+                    Icon(Icons.Filled.Favorite, null)
                 }
             }
             .assertIsSquareWithSize(56.dp)
@@ -111,7 +111,7 @@
             ExtendedFloatingActionButton(
                 modifier = Modifier.testTag("FAB"),
                 text = { Text("Extended FAB Text") },
-                icon = { Icon(Icons.Filled.Favorite) },
+                icon = { Icon(Icons.Filled.Favorite, null) },
                 >
             )
         }
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 9953c9c..dea21b2 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
@@ -30,6 +30,7 @@
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.platform.AmbientDensity
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.captureToImage
@@ -41,6 +42,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -58,7 +60,7 @@
         val vector = Icons.Filled.Menu
         rule
             .setMaterialContentForSizeAssertions {
-                Icon(vector)
+                Icon(vector, null)
             }
             .assertWidthIsEqualTo(width)
             .assertHeightIsEqualTo(height)
@@ -74,7 +76,7 @@
         ).build()
         rule
             .setMaterialContentForSizeAssertions {
-                Icon(vector)
+                Icon(vector, null)
             }
             .assertWidthIsEqualTo(width)
             .assertHeightIsEqualTo(height)
@@ -90,7 +92,7 @@
                     ImageBitmap(width.toIntPx(), height.toIntPx())
                 }
 
-                Icon(image)
+                Icon(image, null)
             }
             .assertWidthIsEqualTo(width)
             .assertHeightIsEqualTo(height)
@@ -107,7 +109,7 @@
                     ImageBitmap(width.toIntPx(), height.toIntPx())
                 }
 
-                Icon(image)
+                Icon(image, null)
             }
             .assertWidthIsEqualTo(width)
             .assertHeightIsEqualTo(height)
@@ -120,7 +122,7 @@
         val painter = ColorPainter(Color.Red)
         rule
             .setMaterialContentForSizeAssertions {
-                Icon(painter)
+                Icon(painter, null)
             }
             .assertWidthIsEqualTo(width)
             .assertHeightIsEqualTo(height)
@@ -138,7 +140,7 @@
                 }
 
                 val imagePainter = ImagePainter(image)
-                Icon(imagePainter)
+                Icon(imagePainter, null)
             }
             .assertWidthIsEqualTo(width)
             .assertHeightIsEqualTo(height)
@@ -160,7 +162,7 @@
                     Color.Red
                 )
             }
-            Icon(image, modifier = Modifier.testTag(testTag), tint = Color.Unspecified)
+            Icon(image, null, modifier = Modifier.testTag(testTag), tint = Color.Unspecified)
         }
 
         // With no color provided for a tint, the icon should render the original pixels
@@ -183,13 +185,31 @@
                     Color.Red
                 )
             }
-            Icon(image, modifier = Modifier.testTag(testTag), tint = Color.Blue)
+            Icon(image, null, modifier = Modifier.testTag(testTag), tint = Color.Blue)
         }
 
         // With a tint color provided, all pixels should be blue
         rule.onNodeWithTag(testTag).captureToImage().assertPixels { Color.Blue }
     }
 
+    @Test
+    fun contentDescriptionAppliedToIcon() {
+        val testTag = "TestTag"
+        rule.setContent {
+            Icon(
+                bitmap = ImageBitmap(100, 100),
+                contentDescription = "qwerty",
+                modifier = Modifier.testTag(testTag)
+            )
+        }
+
+        rule.onNodeWithTag(testTag).fetchSemanticsNode().let {
+            assertThat(it.config.contains(SemanticsProperties.ContentDescription)).isTrue()
+            assertThat(it.config[SemanticsProperties.ContentDescription])
+                .isEqualTo("qwerty")
+        }
+    }
+
     private fun createBitmapWithColor(
         density: Density,
         width: Int,
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 809ca2f..087e32e 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
@@ -27,10 +27,13 @@
 import androidx.compose.ui.node.Ref
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.width
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
@@ -68,7 +71,7 @@
             .setMaterialContentForSizeAssertions {
                 ListItem(
                     text = { Text("Primary text") },
-                    icon = { Icon(icon24x24) }
+                    icon = { Icon(icon24x24, null) }
                 )
             }
             .assertHeightIsEqualTo(expectedHeightSmallIcon)
@@ -82,7 +85,7 @@
             .setMaterialContentForSizeAssertions {
                 ListItem(
                     text = { Text("Primary text") },
-                    icon = { Icon(icon56x56) }
+                    icon = { Icon(icon56x56, null) }
                 )
             }
             .assertHeightIsEqualTo(expectedHeightLargeIcon)
@@ -112,7 +115,7 @@
                 ListItem(
                     text = { Text("Primary text") },
                     secondaryText = { Text("Secondary text") },
-                    icon = { Icon(icon24x24) }
+                    icon = { Icon(icon24x24, null) }
                 )
             }
             .assertHeightIsEqualTo(expectedHeightWithIcon)
@@ -196,11 +199,12 @@
                 ListItem(
                     text = { Text("Primary text", Modifier.saveLayout(textPosition, textSize)) },
                     trailing = {
-                        Image(icon24x24, Modifier.saveLayout(trailingPosition, trailingSize))
+                        Image(icon24x24, null, Modifier.saveLayout(trailingPosition, trailingSize))
                     }
                 )
             }
         }
+        val ds = rule.onRoot().getUnclippedBoundsInRoot()
         rule.runOnIdleWithDensity {
             assertThat(textPosition.value!!.x).isEqualTo(
                 expectedLeftPadding.toIntPx()
@@ -209,9 +213,8 @@
             assertThat(textPosition.value!!.y).isEqualTo(
                 ((listItemHeight.toIntPx() - textSize.value!!.height) / 2f).roundToInt().toFloat()
             )
-            val ds = rule.displaySize
             assertThat(trailingPosition.value!!.x).isEqualTo(
-                ds.width - trailingSize.value!!.width -
+                ds.width.toIntPx() - trailingSize.value!!.width -
                     expectedRightPadding.toIntPx().toFloat()
             )
             assertThat(trailingPosition.value!!.y).isEqualTo(
@@ -235,7 +238,7 @@
             Box {
                 ListItem(
                     text = { Text("Primary text", Modifier.saveLayout(textPosition, textSize)) },
-                    icon = { Image(icon24x24, Modifier.saveLayout(iconPosition, iconSize)) }
+                    icon = { Image(icon24x24, null, Modifier.saveLayout(iconPosition, iconSize)) }
                 )
             }
         }
@@ -301,6 +304,7 @@
                 )
             }
         }
+        val ds = rule.onRoot().getUnclippedBoundsInRoot()
         rule.runOnIdleWithDensity {
             assertThat(textPosition.value!!.x).isEqualTo(
                 expectedLeftPadding.toIntPx().toFloat()
@@ -315,9 +319,8 @@
                 expectedTextBaseline.toIntPx().toFloat() +
                     expectedSecondaryTextBaselineOffset.toIntPx().toFloat()
             )
-            val ds = rule.displaySize
             assertThat(trailingPosition.value!!.x).isEqualTo(
-                ds.width - trailingSize.value!!.width -
+                ds.width.toIntPx() - trailingSize.value!!.width -
                     expectedRightPadding.toIntPx().toFloat()
             )
             assertThat(trailingBaseline.value!!).isEqualTo(
@@ -362,7 +365,7 @@
                         )
                     },
                     icon = {
-                        Image(icon24x24, Modifier.saveLayout(iconPosition, iconSize))
+                        Image(icon24x24, null, Modifier.saveLayout(iconPosition, iconSize))
                     }
                 )
             }
@@ -433,14 +436,15 @@
                         )
                     },
                     icon = {
-                        Image(icon40x40, Modifier.saveLayout(iconPosition, iconSize))
+                        Image(icon40x40, null, Modifier.saveLayout(iconPosition, iconSize))
                     },
                     trailing = {
-                        Image(icon24x24, Modifier.saveLayout(trailingPosition, trailingSize))
+                        Image(icon24x24, null, Modifier.saveLayout(trailingPosition, trailingSize))
                     }
                 )
             }
         }
+        val ds = rule.onRoot().getUnclippedBoundsInRoot()
         rule.runOnIdleWithDensity {
             assertThat(textPosition.value!!.x).isEqualTo(
                 expectedLeftPadding.toIntPx().toFloat() + iconSize.value!!.width +
@@ -464,9 +468,8 @@
             assertThat(iconPosition.value!!.y).isEqualTo(
                 expectedIconTopPadding.toIntPx().toFloat()
             )
-            val ds = rule.displaySize
             assertThat(trailingPosition.value!!.x).isEqualTo(
-                ds.width - trailingSize.value!!.width -
+                ds.width.toIntPx() - trailingSize.value!!.width -
                     expectedRightPadding.toIntPx().toFloat()
             )
             assertThat(trailingPosition.value!!.y).isEqualTo(
@@ -515,14 +518,15 @@
                     },
                     singleLineSecondaryText = false,
                     icon = {
-                        Image(icon24x24, Modifier.saveLayout(iconPosition, iconSize))
+                        Image(icon24x24, null, Modifier.saveLayout(iconPosition, iconSize))
                     },
                     trailing = {
-                        Image(icon24x24, Modifier.saveLayout(trailingPosition, trailingSize))
+                        Image(icon24x24, null, Modifier.saveLayout(trailingPosition, trailingSize))
                     }
                 )
             }
         }
+        val ds = rule.onRoot().getUnclippedBoundsInRoot()
         rule.runOnIdleWithDensity {
             assertThat(textPosition.value!!.x).isEqualTo(
                 expectedLeftPadding.toIntPx().toFloat() + iconSize.value!!.width +
@@ -543,9 +547,8 @@
             assertThat(iconPosition.value!!.y).isEqualTo(
                 expectedIconTopPadding.toIntPx().toFloat()
             )
-            val ds = rule.displaySize
             assertThat(trailingPosition.value!!.x).isEqualTo(
-                ds.width - trailingSize.value!!.width.toFloat() -
+                ds.width.toIntPx() - trailingSize.value!!.width.toFloat() -
                     expectedRightPadding.toIntPx().toFloat()
             )
             assertThat(trailingPosition.value!!.y).isEqualTo(
@@ -610,6 +613,7 @@
                     icon = {
                         Image(
                             icon40x40,
+                            null,
                             Modifier.saveLayout(iconPosition, iconSize)
                         )
                     },
@@ -626,6 +630,8 @@
                 )
             }
         }
+
+        val ds = rule.onRoot().getUnclippedBoundsInRoot()
         rule.runOnIdleWithDensity {
             assertThat(textPosition.value!!.x).isEqualTo(
                 expectedLeftPadding.toIntPx().toFloat() +
@@ -660,9 +666,8 @@
             assertThat(iconPosition.value!!.y).isEqualTo(
                 expectedIconTopPadding.toIntPx().toFloat()
             )
-            val ds = rule.displaySize
             assertThat(trailingPosition.value!!.x).isEqualTo(
-                ds.width - trailingSize.value!!.width -
+                ds.width.toIntPx() - trailingSize.value!!.width -
                     expectedRightPadding.toIntPx().toFloat()
             )
             assertThat(trailingBaseline.value!!).isEqualTo(
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MaterialTextSelectionColorsScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MaterialTextSelectionColorsScreenshotTest.kt
index 6cbca52..4f967de 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MaterialTextSelectionColorsScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MaterialTextSelectionColorsScreenshotTest.kt
@@ -17,6 +17,7 @@
 package androidx.compose.material
 
 import android.os.Build
+import androidx.compose.foundation.layout.width
 import androidx.compose.runtime.Composable
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
@@ -34,6 +35,7 @@
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performGesture
 import androidx.compose.ui.test.width
+import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -248,7 +250,7 @@
 private fun FilledTextFieldTestContent(colors: Colors) {
     MaterialTheme(colors) {
         Surface(Modifier.testTag(Tag)) {
-            TextField(value = Text, >
+            TextField(value = Text,  modifier = Modifier.width(280.dp))
         }
     }
 }
@@ -257,7 +259,7 @@
 private fun OutlinedTextFieldTestContent(colors: Colors) {
     MaterialTheme(colors) {
         Surface(Modifier.testTag(Tag)) {
-            OutlinedTextField(value = Text, >
+            OutlinedTextField(value = Text,  modifier = Modifier.width(280.dp))
         }
     }
 }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ScaffoldScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ScaffoldScreenshotTest.kt
index 0deb853..9600b81 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ScaffoldScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ScaffoldScreenshotTest.kt
@@ -48,7 +48,6 @@
 @LargeTest
 @RunWith(AndroidJUnit4::class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-@OptIn(ExperimentalMaterialApi::class)
 class ScaffoldScreenshotTest {
 
     @get:Rule
@@ -602,7 +601,6 @@
  * @param rtl whether to set [LayoutDirection.Rtl] as the [LayoutDirection] for this Scaffold and
  * its content
  */
-@OptIn(ExperimentalMaterialApi::class)
 @Composable
 private fun ScreenshotScaffold(
     showTopAppBar: Boolean,
@@ -625,7 +623,7 @@
             val cutoutShape = if (fabCutout) CircleShape else null
             BottomAppBar(cutoutShape = cutoutShape) {
                 IconButton( {
-                    Icon(Icons.Filled.Menu)
+                    Icon(Icons.Filled.Menu, null)
                 }
             }
         }
@@ -647,7 +645,7 @@
     val fab = @Composable {
         if (showFab) {
             FloatingActionButton(
-                content = { Icon(Icons.Filled.Favorite) },
+                content = { Icon(Icons.Filled.Favorite, null) },
                 >
             )
         }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ScaffoldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ScaffoldTest.kt
index aa6181f0..d33dc24 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ScaffoldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ScaffoldTest.kt
@@ -285,7 +285,7 @@
                         },
                         >
                     ) {
-                        Icon(Icons.Filled.Favorite)
+                        Icon(Icons.Filled.Favorite, null)
                     }
                 },
                 floatingActionButtonPosition = FabPosition.Center,
@@ -321,7 +321,7 @@
                         },
                         >
                     ) {
-                        Icon(Icons.Filled.Favorite)
+                        Icon(Icons.Filled.Favorite, null)
                     }
                 },
                 floatingActionButtonPosition = FabPosition.End,
@@ -394,7 +394,7 @@
                         },
                         >
                     ) {
-                        Icon(Icons.Filled.Favorite)
+                        Icon(Icons.Filled.Favorite, null)
                     }
                 }
             }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
index 87ef7af..84b8fc6 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
@@ -37,7 +37,6 @@
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 @LargeTest
-@OptIn(ExperimentalMaterialApi::class)
 class SnackbarHostTest {
 
     @get:Rule
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarTest.kt
index b8281ba..287c882 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SnackbarTest.kt
@@ -365,7 +365,6 @@
     }
 
     @Test
-    @OptIn(ExperimentalMaterialApi::class)
     fun defaultSnackbar_dataVersion_proxiesParameters() {
         var clicked = false
         val snackbarData = object : SnackbarData {
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TabTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TabTest.kt
index 65e2389..ea0bd06 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TabTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TabTest.kt
@@ -114,7 +114,7 @@
     fun iconTab_height() {
         rule
             .setMaterialContentForSizeAssertions {
-                Tab(icon = { Icon(icon) }, selected = true, >
+                Tab(icon = { Icon(icon, null) }, selected = true, >
             }
             .assertHeightIsEqualTo(ExpectedSmallTabHeight)
     }
@@ -126,7 +126,7 @@
                 Surface {
                     Tab(
                         text = { Text("Text and Icon") },
-                        icon = { Icon(icon) },
+                        icon = { Icon(icon, null) },
                         selected = true,
                         >
                     )
@@ -245,7 +245,7 @@
                             text = {
                                 Text(title, Modifier.testTag("text"))
                             },
-                            icon = { Icon(Icons.Filled.Favorite) },
+                            icon = { Icon(Icons.Filled.Favorite, null) },
                             selected = state == index,
                              state = index }
                         )
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt
index 285e619..690973d 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt
@@ -80,7 +80,8 @@
                 OutlinedTextField(
                     value = "Text",
                     >
-                    label = { Text("Label") }
+                    label = { Text("Label") },
+                    modifier = Modifier.width(280.dp)
                 )
             }
         }
@@ -95,7 +96,8 @@
                 OutlinedTextField(
                     value = "",
                     >
-                    label = { Text("Label") }
+                    label = { Text("Label") },
+                    modifier = Modifier.width(280.dp)
                 )
             }
         }
@@ -110,7 +112,8 @@
                 OutlinedTextField(
                     value = "",
                     >
-                    label = { Text("Label") }
+                    label = { Text("Label") },
+                    modifier = Modifier.width(280.dp)
                 )
             }
         }
@@ -128,7 +131,8 @@
                     OutlinedTextField(
                         value = "",
                         >
-                        label = { Text("Label") }
+                        label = { Text("Label") },
+                        modifier = Modifier.width(280.dp)
                     )
                 }
             }
@@ -147,7 +151,8 @@
                     value = "Input",
                     >
                     label = { Text("Label") },
-                    isErrorValue = true
+                    isErrorValue = true,
+                    modifier = Modifier.width(280.dp)
                 )
             }
         }
@@ -165,7 +170,8 @@
                     value = "",
                     >
                     label = { Text("Label") },
-                    isErrorValue = true
+                    isErrorValue = true,
+                    modifier = Modifier.width(280.dp)
                 )
             }
         }
@@ -180,7 +186,7 @@
                 OutlinedTextField(
                     value = "Hello, world!",
                     >
-                    modifier = Modifier.testTag(TextFieldTag)
+                    modifier = Modifier.testTag(TextFieldTag).width(280.dp)
                 )
             }
         }
@@ -195,7 +201,7 @@
                 value = "Text",
                 >
                 label = { Text("Label") },
-                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+                modifier = Modifier.height(300.dp).width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -208,7 +214,7 @@
             OutlinedTextField(
                 value = "Text",
                 >
-                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+                modifier = Modifier.height(300.dp).width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -223,7 +229,7 @@
                 >
                 label = { Text("Label") },
                 placeholder = { Text("placeholder") },
-                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+                modifier = Modifier.height(300.dp).width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -239,7 +245,7 @@
                 value = "",
                 >
                 placeholder = { Text("placeholder") },
-                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+                modifier = Modifier.height(300.dp).width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -255,7 +261,7 @@
                 value = "",
                 >
                 label = { Text("Label") },
-                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+                modifier = Modifier.height(300.dp).width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -270,7 +276,7 @@
                 >
                 singleLine = true,
                 label = { Text("Label") },
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.testTag(TextFieldTag).width(280.dp)
             )
         }
 
@@ -284,7 +290,7 @@
                 value = "Text",
                 >
                 singleLine = true,
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.testTag(TextFieldTag).width(280.dp)
             )
         }
 
@@ -300,7 +306,7 @@
                 placeholder = { Text("placeholder") },
                 label = { Text("Label") },
                 singleLine = true,
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.testTag(TextFieldTag).width(280.dp)
             )
         }
 
@@ -317,7 +323,7 @@
                 >
                 placeholder = { Text("placeholder") },
                 singleLine = true,
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.testTag(TextFieldTag).width(280.dp)
             )
         }
 
@@ -335,7 +341,7 @@
                 value = "",
                 >
                 label = { Text("Label") },
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.testTag(TextFieldTag).width(280.dp)
             )
         }
 
@@ -350,7 +356,8 @@
                     value = TextFieldValue("Text"),
                     >
                     singleLine = true,
-                    enabled = false
+                    enabled = false,
+                    modifier = Modifier.width(280.dp)
                 )
             }
         }
@@ -366,7 +373,8 @@
                     value = TextFieldValue("Text"),
                     >
                     singleLine = true,
-                    enabled = false
+                    enabled = false,
+                    modifier = Modifier.width(280.dp)
                 )
             }
         }
@@ -401,7 +409,7 @@
             OutlinedTextField(
                 value = TextFieldValue("Text"),
                 >
-                modifier = Modifier.testTag(TextFieldTag),
+                modifier = Modifier.testTag(TextFieldTag).width(280.dp),
                 enabled = true,
                 readOnly = true
             )
@@ -416,7 +424,7 @@
             OutlinedTextField(
                 value = TextFieldValue("Text"),
                 >
-                modifier = Modifier.testTag(TextFieldTag),
+                modifier = Modifier.testTag(TextFieldTag).width(280.dp),
                 enabled = true,
                 readOnly = true
             )
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
index 1a96cf0..238014e 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
@@ -22,6 +22,7 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.preferredWidth
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material.AmbientContentAlpha
 import androidx.compose.material.AmbientContentColor
@@ -29,9 +30,11 @@
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.OutlinedTextField
 import androidx.compose.material.Text
+import androidx.compose.material.TextField
 import androidx.compose.material.TextFieldPadding
 import androidx.compose.material.runOnIdleWithDensity
 import androidx.compose.material.setMaterialContent
+import androidx.compose.material.setMaterialContentForSizeAssertions
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -48,6 +51,7 @@
 import androidx.compose.ui.platform.AmbientTextInputService
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.click
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -84,6 +88,7 @@
 @OptIn(ExperimentalTestApi::class)
 class OutlinedTextFieldTest {
     private val ExpectedMinimumTextFieldHeight = 56.dp
+    private val ExpectedDefaultTextFieldWidth = 280.dp
     private val ExpectedPadding = 16.dp
     private val IconPadding = 12.dp
     private val IconColorAlpha = 0.54f
@@ -93,6 +98,31 @@
     val rule = createComposeRule()
 
     @Test
+    fun testTextField_setSmallWidth() {
+        rule.setMaterialContentForSizeAssertions {
+            TextField(
+                value = "input",
+                >
+                label = {},
+                modifier = Modifier.width(40.dp)
+            )
+        }
+            .assertWidthIsEqualTo(40.dp)
+    }
+
+    @Test
+    fun testTextField_defaultWidth() {
+        rule.setMaterialContentForSizeAssertions {
+            TextField(
+                value = "input",
+                >
+                label = {}
+            )
+        }
+            .assertWidthIsEqualTo(ExpectedDefaultTextFieldWidth)
+    }
+
+    @Test
     fun testOutlinedTextFields_singleFocus() {
         var textField1Focused = false
         val textField1Tag = "TextField1"
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt
index 96c7d79..77c1ac7 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt
@@ -79,7 +79,8 @@
                 TextField(
                     value = "Text",
                     >
-                    label = { Text("Label") }
+                    label = { Text("Label") },
+                    modifier = Modifier.width(280.dp)
                 )
             }
         }
@@ -94,7 +95,8 @@
                 TextField(
                     value = "",
                     >
-                    label = { Text("Label") }
+                    label = { Text("Label") },
+                    modifier = Modifier.width(280.dp)
                 )
             }
         }
@@ -109,7 +111,8 @@
                 TextField(
                     value = "",
                     >
-                    label = { Text("Label") }
+                    label = { Text("Label") },
+                    modifier = Modifier.width(280.dp)
                 )
             }
         }
@@ -127,7 +130,8 @@
                     TextField(
                         value = "",
                         >
-                        label = { Text("Label") }
+                        label = { Text("Label") },
+                        modifier = Modifier.width(280.dp)
                     )
                 }
             }
@@ -146,7 +150,7 @@
                 >
                 label = { Text("Label") },
                 isErrorValue = true,
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -163,7 +167,7 @@
                 >
                 label = { Text("Label") },
                 isErrorValue = true,
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -177,7 +181,7 @@
                 TextField(
                     value = "Hello, world!",
                     >
-                    modifier = Modifier.testTag(TextFieldTag)
+                    modifier = Modifier.width(280.dp).testTag(TextFieldTag)
                 )
             }
         }
@@ -192,7 +196,7 @@
                 value = "Text",
                 >
                 label = { Text("Label") },
-                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+                modifier = Modifier.height(300.dp).width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -205,7 +209,7 @@
             TextField(
                 value = "Text",
                 >
-                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+                modifier = Modifier.height(300.dp).width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -220,7 +224,7 @@
                 >
                 label = { Text("Label") },
                 placeholder = { Text("placeholder") },
-                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+                modifier = Modifier.height(300.dp).width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -236,7 +240,7 @@
                 value = "",
                 >
                 placeholder = { Text("placeholder") },
-                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+                modifier = Modifier.height(300.dp).width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -252,7 +256,7 @@
                 value = "",
                 >
                 label = { Text("Label") },
-                modifier = Modifier.height(300.dp).testTag(TextFieldTag)
+                modifier = Modifier.height(300.dp).width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -267,7 +271,7 @@
                 >
                 singleLine = true,
                 label = { Text("Label") },
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -281,7 +285,7 @@
                 value = "Text",
                 >
                 singleLine = true,
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -297,7 +301,7 @@
                 placeholder = { Text("placeholder") },
                 label = { Text("Label") },
                 singleLine = true,
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -314,7 +318,7 @@
                 >
                 placeholder = { Text("placeholder") },
                 singleLine = true,
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -332,7 +336,7 @@
                 value = "",
                 >
                 label = { Text("Label") },
-                modifier = Modifier.testTag(TextFieldTag)
+                modifier = Modifier.width(280.dp).testTag(TextFieldTag)
             )
         }
 
@@ -345,7 +349,7 @@
             TextField(
                 value = TextFieldValue("Text"),
                 >
-                modifier = Modifier.testTag(TextFieldTag),
+                modifier = Modifier.width(280.dp).testTag(TextFieldTag),
                 singleLine = true,
                 enabled = false
             )
@@ -361,7 +365,7 @@
                 value = TextFieldValue("Text"),
                 >
                 singleLine = true,
-                modifier = Modifier.testTag(TextFieldTag),
+                modifier = Modifier.width(280.dp).testTag(TextFieldTag),
                 enabled = false
             )
         }
@@ -394,7 +398,7 @@
             TextField(
                 value = TextFieldValue("Text"),
                 >
-                modifier = Modifier.testTag(TextFieldTag),
+                modifier = Modifier.width(280.dp).testTag(TextFieldTag),
                 enabled = true,
                 readOnly = true
             )
@@ -409,7 +413,7 @@
             TextField(
                 value = TextFieldValue("Text"),
                 >
-                modifier = Modifier.testTag(TextFieldTag),
+                modifier = Modifier.width(280.dp).testTag(TextFieldTag),
                 enabled = true,
                 readOnly = true
             )
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
index d7af753..bf96756 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
@@ -20,12 +20,15 @@
 import android.os.Build
 import android.view.View
 import android.view.inputmethod.InputMethodManager
+import androidx.compose.foundation.Interaction
+import androidx.compose.foundation.InteractionState
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredSize
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material.AmbientContentAlpha
 import androidx.compose.material.AmbientContentColor
@@ -46,8 +49,6 @@
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusModifier
 import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.focus.isFocused
-import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
@@ -60,6 +61,7 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.click
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -90,8 +92,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
 import kotlin.math.roundToInt
 
 @MediumTest
@@ -99,7 +99,8 @@
 @OptIn(ExperimentalTestApi::class)
 class TextFieldTest {
 
-    private val ExpectedMinimumTextFieldHeight = 56.dp
+    private val ExpectedDefaultTextFieldHeight = 56.dp
+    private val ExpectedDefaultTextFieldWidth = 280.dp
     private val ExpectedPadding = 16.dp
     private val IconPadding = 12.dp
     private val ExpectedBaselineOffset = 20.dp
@@ -120,34 +121,56 @@
                 modifier = Modifier.preferredHeight(20.dp)
             )
         }
-            .assertHeightIsEqualTo(ExpectedMinimumTextFieldHeight)
+            .assertHeightIsEqualTo(20.dp)
+    }
+
+    @Test
+    fun testTextField_setSmallWidth() {
+        rule.setMaterialContentForSizeAssertions {
+            TextField(
+                value = "input",
+                >
+                label = {},
+                modifier = Modifier.width(40.dp)
+            )
+        }
+            .assertWidthIsEqualTo(40.dp)
+    }
+
+    @Test
+    fun testTextField_defaultWidth() {
+        rule.setMaterialContentForSizeAssertions {
+            TextField(
+                value = "input",
+                >
+                label = {}
+            )
+        }
+            .assertWidthIsEqualTo(ExpectedDefaultTextFieldWidth)
     }
 
     @Test
     fun testTextFields_singleFocus() {
-        var textField1Focused = false
         val textField1Tag = "TextField1"
-
-        var textField2Focused = false
         val textField2Tag = "TextField2"
+        val interactionState1 = InteractionState()
+        val interactionState2 = InteractionState()
 
         rule.setMaterialContent {
             Column {
                 TextField(
-                    modifier = Modifier
-                        .onFocusChanged { textField1Focused = it.isFocused }
-                        .testTag(textField1Tag),
+                    modifier = Modifier.testTag(textField1Tag),
                     value = "input1",
                     >
-                    label = {}
+                    label = {},
+                    interactionState = interactionState1
                 )
                 TextField(
-                    modifier = Modifier
-                        .onFocusChanged { textField2Focused = it.isFocused }
-                        .testTag(textField2Tag),
+                    modifier = Modifier.testTag(textField2Tag),
                     value = "input2",
                     >
-                    label = {}
+                    label = {},
+                    interactionState = interactionState2
                 )
             }
         }
@@ -155,32 +178,29 @@
         rule.onNodeWithTag(textField1Tag).performClick()
 
         rule.runOnIdle {
-            assertThat(textField1Focused).isTrue()
-            assertThat(textField2Focused).isFalse()
+            assertThat(interactionState1.contains(Interaction.Focused)).isTrue()
+            assertThat(interactionState2.contains(Interaction.Focused)).isFalse()
         }
 
         rule.onNodeWithTag(textField2Tag).performClick()
 
         rule.runOnIdle {
-            assertThat(textField1Focused).isFalse()
-            assertThat(textField2Focused).isTrue()
+            assertThat(interactionState1.contains(Interaction.Focused)).isFalse()
+            assertThat(interactionState2.contains(Interaction.Focused)).isTrue()
         }
     }
 
     @Test
     fun testTextField_getFocus_whenClickedOnSurfaceArea() {
-        var focused = false
+        val interactionState = InteractionState()
         rule.setMaterialContent {
-            Box {
-                TextField(
-                    modifier = Modifier
-                        .onFocusChanged { focused = it.isFocused }
-                        .testTag(TextfieldTag),
-                    value = "input",
-                    >
-                    label = {}
-                )
-            }
+            TextField(
+                modifier = Modifier.testTag(TextfieldTag),
+                value = "input",
+                >
+                label = {},
+                interactionState = interactionState
+            )
         }
 
         // Click on (2, 2) which is Surface area and outside input area
@@ -189,7 +209,7 @@
         }
 
         rule.runOnIdleWithDensity {
-            assertThat(focused).isTrue()
+            assertThat(interactionState.contains(Interaction.Focused)).isTrue()
         }
     }
 
@@ -294,7 +314,7 @@
                 ExpectedPadding.toIntPx().toFloat()
             )
             assertThat(labelPosition.value?.y).isEqualTo(
-                ((ExpectedMinimumTextFieldHeight.toIntPx() - labelSize.value!!.height) / 2f)
+                ((ExpectedDefaultTextFieldHeight.toIntPx() - labelSize.value!!.height) / 2f)
                     .roundToInt().toFloat()
             )
         }
@@ -857,21 +877,20 @@
     @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun testTextField_alphaNotApplied_toCustomBackgroundColorAndTransparentColors() {
-        val latch = CountDownLatch(1)
+        val interactionState = InteractionState()
 
         rule.setMaterialContent {
             Box(Modifier.background(color = Color.White)) {
                 TextField(
-                    modifier = Modifier
-                        .onFocusChanged { if (it.isFocused) latch.countDown() }
-                        .testTag(TextfieldTag),
+                    modifier = Modifier.testTag(TextfieldTag),
                     value = "",
                     >
                     label = {},
                     shape = RectangleShape,
                     backgroundColor = Color.Blue,
                     activeColor = Color.Transparent,
-                    inactiveColor = Color.Transparent
+                    inactiveColor = Color.Transparent,
+                    interactionState = interactionState
                 )
             }
         }
@@ -888,7 +907,9 @@
             )
 
         rule.onNodeWithTag(TextfieldTag).performClick()
-        assert(latch.await(1, TimeUnit.SECONDS))
+        rule.runOnIdle {
+            assertThat(interactionState.contains(Interaction.Focused)).isTrue()
+        }
 
         rule.onNodeWithTag(TextfieldTag)
             .captureToImage()
@@ -953,11 +974,12 @@
         }
     }
 
-    private val View.isSoftwareKeyboardShown: Boolean get() {
-        val inputMethodManager =
-            context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
-        // TODO(b/163742556): This is just a proxy for software keyboard visibility. Find a better
-        //  way to check if the software keyboard is shown.
-        return inputMethodManager.isAcceptingText()
-    }
+    private val View.isSoftwareKeyboardShown: Boolean
+        get() {
+            val inputMethodManager =
+                context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+            // TODO(b/163742556): This is just a proxy for software keyboard visibility. Find a better
+            //  way to check if the software keyboard is shown.
+            return inputMethodManager.isAcceptingText()
+        }
 }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Button.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Button.kt
index 9354d16..29b778a 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Button.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Button.kt
@@ -18,10 +18,7 @@
 
 package androidx.compose.material
 
-import androidx.compose.animation.AnimatedValueModel
-import androidx.compose.animation.asDisposableClock
-import androidx.compose.animation.core.AnimationClockObservable
-import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.VectorConverter
 import androidx.compose.foundation.AmbientIndication
 import androidx.compose.foundation.BorderStroke
@@ -37,15 +34,18 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.compositeOver
-import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
@@ -108,13 +108,13 @@
     // the ripple below the clip once http://b/157687898 is fixed and we have
     // more flexibility to move the clickable modifier (see candidate approach
     // aosp/1361921)
-    val contentColor = colors.contentColor(enabled)
+    val contentColor by colors.contentColor(enabled)
     Surface(
         shape = shape,
-        color = colors.backgroundColor(enabled),
+        color = colors.backgroundColor(enabled).value,
         contentColor = contentColor.copy(alpha = 1f),
         border = border,
-        elevation = elevation?.elevation(enabled, interactionState) ?: 0.dp,
+        elevation = elevation?.elevation(enabled, interactionState)?.value ?: 0.dp,
         modifier = modifier.clickable(
             >
             enabled = enabled,
@@ -281,7 +281,8 @@
      * @param enabled whether the button is enabled
      * @param interactionState the [InteractionState] for this button
      */
-    fun elevation(enabled: Boolean, interactionState: InteractionState): Dp
+    @Composable
+    fun elevation(enabled: Boolean, interactionState: InteractionState): State<Dp>
 }
 
 /**
@@ -300,14 +301,16 @@
      *
      * @param enabled whether the button is enabled
      */
-    fun backgroundColor(enabled: Boolean): Color
+    @Composable
+    fun backgroundColor(enabled: Boolean): State<Color>
 
     /**
      * Represents the content color for this button, depending on [enabled].
      *
      * @param enabled whether the button is enabled
      */
-    fun contentColor(enabled: Boolean): Color
+    @Composable
+    fun contentColor(enabled: Boolean): State<Color>
 }
 
 /**
@@ -373,13 +376,11 @@
         // hovered: Dp = 4.dp,
         disabledElevation: Dp = 0.dp
     ): ButtonElevation {
-        val clock = AmbientAnimationClock.current.asDisposableClock()
-        return remember(defaultElevation, pressedElevation, disabledElevation, clock) {
+        return remember(defaultElevation, pressedElevation, disabledElevation) {
             DefaultButtonElevation(
                 defaultElevation = defaultElevation,
                 pressedElevation = pressedElevation,
-                disabledElevation = disabledElevation,
-                clock = clock
+                disabledElevation = disabledElevation
             )
         }
     }
@@ -492,13 +493,9 @@
     private val defaultElevation: Dp,
     private val pressedElevation: Dp,
     private val disabledElevation: Dp,
-    private val clock: AnimationClockObservable
 ) : ButtonElevation {
-    private val lazyAnimatedElevation = LazyAnimatedValue<Dp, AnimationVector1D> { target ->
-        AnimatedValueModel(initialValue = target, typeConverter = Dp.VectorConverter, clock = clock)
-    }
-
-    override fun elevation(enabled: Boolean, interactionState: InteractionState): Dp {
+    @Composable
+    override fun elevation(enabled: Boolean, interactionState: InteractionState): State<Dp> {
         val interaction = interactionState.value.lastOrNull {
             it is Interaction.Pressed
         }
@@ -512,18 +509,18 @@
             }
         }
 
-        val animatedElevation = lazyAnimatedElevation.animatedValueForTarget(target)
+        val animatable = remember { Animatable(target, Dp.VectorConverter) }
 
-        if (animatedElevation.targetValue != target) {
-            if (!enabled) {
-                // No transition when moving to a disabled state
-                animatedElevation.snapTo(target)
-            } else {
-                val lastInteraction = when (animatedElevation.targetValue) {
+        if (!enabled) {
+            // No transition when moving to a disabled state
+            animatable.snapTo(target)
+        } else {
+            LaunchedEffect(target) {
+                val lastInteraction = when (animatable.targetValue) {
                     pressedElevation -> Interaction.Pressed
                     else -> null
                 }
-                animatedElevation.animateElevation(
+                animatable.animateElevation(
                     from = lastInteraction,
                     to = interaction,
                     target = target
@@ -531,7 +528,7 @@
             }
         }
 
-        return animatedElevation.value
+        return animatable.asState()
     }
 }
 
@@ -546,12 +543,14 @@
     private val contentColor: Color,
     private val disabledContentColor: Color
 ) : ButtonColors {
-    override fun backgroundColor(enabled: Boolean): Color {
-        return if (enabled) backgroundColor else disabledBackgroundColor
+    @Composable
+    override fun backgroundColor(enabled: Boolean): State<Color> {
+        return rememberUpdatedState(if (enabled) backgroundColor else disabledBackgroundColor)
     }
 
-    override fun contentColor(enabled: Boolean): Color {
-        return if (enabled) contentColor else disabledContentColor
+    @Composable
+    override fun contentColor(enabled: Boolean): State<Color> {
+        return rememberUpdatedState(if (enabled) contentColor else disabledContentColor)
     }
 
     override fun equals(other: Any?): Boolean {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Checkbox.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Checkbox.kt
index d2493d0..78915f1 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Checkbox.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Checkbox.kt
@@ -16,11 +16,7 @@
 
 package androidx.compose.material
 
-import androidx.compose.animation.AnimatedValueModel
-import androidx.compose.animation.VectorConverter
-import androidx.compose.animation.asDisposableClock
-import androidx.compose.animation.core.AnimationClockObservable
-import androidx.compose.animation.core.AnimationVector4D
+import androidx.compose.animation.animateAsState
 import androidx.compose.animation.core.FloatPropKey
 import androidx.compose.animation.core.TransitionSpec
 import androidx.compose.animation.core.keyframes
@@ -39,7 +35,10 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.CornerRadius
@@ -52,7 +51,6 @@
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.graphics.drawscope.Fill
 import androidx.compose.ui.graphics.drawscope.Stroke
-import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.state.ToggleableState
 import androidx.compose.ui.unit.dp
@@ -169,7 +167,8 @@
      *
      * @param state the [ToggleableState] of the checkbox
      */
-    fun checkmarkColor(state: ToggleableState): Color
+    @Composable
+    fun checkmarkColor(state: ToggleableState): State<Color>
 
     /**
      * Represents the color used for the box (background) of the checkbox, depending on [enabled]
@@ -178,7 +177,8 @@
      * @param enabled whether the checkbox is enabled or not
      * @param state the [ToggleableState] of the checkbox
      */
-    fun boxColor(enabled: Boolean, state: ToggleableState): Color
+    @Composable
+    fun boxColor(enabled: Boolean, state: ToggleableState): State<Color>
 
     /**
      * Represents the color used for the border of the checkbox, depending on [enabled] and [state].
@@ -186,7 +186,8 @@
      * @param enabled whether the checkbox is enabled or not
      * @param state the [ToggleableState] of the checkbox
      */
-    fun borderColor(enabled: Boolean, state: ToggleableState): Color
+    @Composable
+    fun borderColor(enabled: Boolean, state: ToggleableState): State<Color>
 }
 
 /**
@@ -213,14 +214,12 @@
         disabledColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled),
         disabledIndeterminateColor: Color = checkedColor.copy(alpha = ContentAlpha.disabled)
     ): CheckboxColors {
-        val clock = AmbientAnimationClock.current.asDisposableClock()
         return remember(
             checkedColor,
             uncheckedColor,
             checkmarkColor,
             disabledColor,
             disabledIndeterminateColor,
-            clock
         ) {
             DefaultCheckboxColors(
                 checkedBorderColor = checkedColor,
@@ -234,7 +233,6 @@
                 uncheckedBorderColor = uncheckedColor,
                 disabledBorderColor = disabledColor,
                 disabledIndeterminateBorderColor = disabledIndeterminateColor,
-                clock = clock
             )
         }
     }
@@ -250,11 +248,10 @@
 ) {
     val state = transition(definition = TransitionDefinition, toState = value)
     val checkCache = remember { CheckDrawingCache() }
+    val checkColor by colors.checkmarkColor(value)
+    val boxColor by colors.boxColor(enabled, value)
+    val borderColor by colors.borderColor(enabled, value)
     Canvas(modifier.wrapContentSize(Alignment.Center).size(CheckboxSize)) {
-        val checkColor = colors.checkmarkColor(value)
-        val boxColor = colors.boxColor(enabled, value)
-        val borderColor = colors.borderColor(enabled, value)
-
         val strokeWidthPx = StrokeWidth.toPx()
         drawBox(
             boxColor = boxColor,
@@ -357,36 +354,22 @@
     private val checkedBorderColor: Color,
     private val uncheckedBorderColor: Color,
     private val disabledBorderColor: Color,
-    private val disabledIndeterminateBorderColor: Color,
-    private val clock: AnimationClockObservable
+    private val disabledIndeterminateBorderColor: Color
 ) : CheckboxColors {
-    private val lazyAnimatedCheckmarkColor = LazyAnimatedValue<Color, AnimationVector4D> { target ->
-        AnimatedValueModel(target, (Color.VectorConverter)(target.colorSpace), clock)
-    }
-    private val lazyAnimatedBoxColor = LazyAnimatedValue<Color, AnimationVector4D> { target ->
-        AnimatedValueModel(target, (Color.VectorConverter)(target.colorSpace), clock)
-    }
-    private val lazyAnimatedBorderColor = LazyAnimatedValue<Color, AnimationVector4D> { target ->
-        AnimatedValueModel(target, (Color.VectorConverter)(target.colorSpace), clock)
-    }
-
-    override fun checkmarkColor(state: ToggleableState): Color {
+    @Composable
+    override fun checkmarkColor(state: ToggleableState): State<Color> {
         val target = if (state == ToggleableState.Off) {
             uncheckedCheckmarkColor
         } else {
             checkedCheckmarkColor
         }
 
-        val animatedCheckmarkColor = lazyAnimatedCheckmarkColor.animatedValueForTarget(target)
-
-        if (animatedCheckmarkColor.targetValue != target) {
-            val duration = if (state == ToggleableState.Off) BoxOutDuration else BoxInDuration
-            animatedCheckmarkColor.animateTo(target, tween(durationMillis = duration))
-        }
-        return animatedCheckmarkColor.value
+        val duration = if (state == ToggleableState.Off) BoxOutDuration else BoxInDuration
+        return animateAsState(target, tween(durationMillis = duration))
     }
 
-    override fun boxColor(enabled: Boolean, state: ToggleableState): Color {
+    @Composable
+    override fun boxColor(enabled: Boolean, state: ToggleableState): State<Color> {
         val target = if (enabled) {
             when (state) {
                 ToggleableState.On, ToggleableState.Indeterminate -> checkedBoxColor
@@ -403,19 +386,15 @@
         // If not enabled 'snap' to the disabled state, as there should be no animations between
         // enabled / disabled.
         return if (enabled) {
-            val animatedBoxColor = lazyAnimatedBoxColor.animatedValueForTarget(target)
-
-            if (animatedBoxColor.targetValue != target) {
-                val duration = if (state == ToggleableState.Off) BoxOutDuration else BoxInDuration
-                animatedBoxColor.animateTo(target, tween(durationMillis = duration))
-            }
-            animatedBoxColor.value
+            val duration = if (state == ToggleableState.Off) BoxOutDuration else BoxInDuration
+            animateAsState(target, tween(durationMillis = duration))
         } else {
-            target
+            rememberUpdatedState(target)
         }
     }
 
-    override fun borderColor(enabled: Boolean, state: ToggleableState): Color {
+    @Composable
+    override fun borderColor(enabled: Boolean, state: ToggleableState): State<Color> {
         val target = if (enabled) {
             when (state) {
                 ToggleableState.On, ToggleableState.Indeterminate -> checkedBorderColor
@@ -431,15 +410,10 @@
         // If not enabled 'snap' to the disabled state, as there should be no animations between
         // enabled / disabled.
         return if (enabled) {
-            val animatedBorderColor = lazyAnimatedBorderColor.animatedValueForTarget(target)
-
-            if (animatedBorderColor.targetValue != target) {
-                val duration = if (state == ToggleableState.Off) BoxOutDuration else BoxInDuration
-                animatedBorderColor.animateTo(target, tween(durationMillis = duration))
-            }
-            animatedBorderColor.value
+            val duration = if (state == ToggleableState.Off) BoxOutDuration else BoxInDuration
+            animateAsState(target, tween(durationMillis = duration))
         } else {
-            target
+            rememberUpdatedState(target)
         }
     }
 }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Elevation.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Elevation.kt
index 3f8da84..afb38dc 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Elevation.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Elevation.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.material
 
-import androidx.compose.animation.core.AnimatedValue
+import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.CubicBezierEasing
 import androidx.compose.animation.core.FastOutSlowInEasing
@@ -40,7 +40,7 @@
  * @param target the [Dp] target elevation for this component, corresponding to the elevation
  * desired for the [to] state.
  */
-fun AnimatedValue<Dp, *>.animateElevation(
+suspend fun Animatable<Dp, *>.animateElevation(
     from: Interaction? = null,
     to: Interaction? = null,
     target: Dp
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/FloatingActionButton.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/FloatingActionButton.kt
index 7ccbb7e..f883ac1 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/FloatingActionButton.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/FloatingActionButton.kt
@@ -16,10 +16,7 @@
 
 package androidx.compose.material
 
-import androidx.compose.animation.AnimatedValueModel
-import androidx.compose.animation.asDisposableClock
-import androidx.compose.animation.core.AnimationClockObservable
-import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.VectorConverter
 import androidx.compose.foundation.AmbientIndication
 import androidx.compose.foundation.Interaction
@@ -35,14 +32,15 @@
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.shape.CornerSize
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
@@ -96,7 +94,7 @@
         shape = shape,
         color = backgroundColor,
         contentColor = contentColor,
-        elevation = elevation.elevation(interactionState)
+        elevation = elevation.elevation(interactionState).value
     ) {
         Providers(AmbientContentAlpha provides contentColor.alpha) {
             ProvideTextStyle(MaterialTheme.typography.button) {
@@ -201,7 +199,8 @@
      *
      * @param interactionState the [InteractionState] for this floating action button
      */
-    fun elevation(interactionState: InteractionState): Dp
+    @Composable
+    fun elevation(interactionState: InteractionState): State<Dp>
 }
 
 /**
@@ -226,12 +225,10 @@
         // focused: Dp = 8.dp,
         // hovered: Dp = 8.dp,
     ): FloatingActionButtonElevation {
-        val clock = AmbientAnimationClock.current.asDisposableClock()
-        return remember(defaultElevation, pressedElevation, clock) {
+        return remember(defaultElevation, pressedElevation) {
             DefaultFloatingActionButtonElevation(
                 defaultElevation = defaultElevation,
-                pressedElevation = pressedElevation,
-                clock = clock
+                pressedElevation = pressedElevation
             )
         }
     }
@@ -245,13 +242,9 @@
 private class DefaultFloatingActionButtonElevation(
     private val defaultElevation: Dp,
     private val pressedElevation: Dp,
-    private val clock: AnimationClockObservable
 ) : FloatingActionButtonElevation {
-    private val lazyAnimatedElevation = LazyAnimatedValue<Dp, AnimationVector1D> { target ->
-        AnimatedValueModel(initialValue = target, typeConverter = Dp.VectorConverter, clock = clock)
-    }
-
-    override fun elevation(interactionState: InteractionState): Dp {
+    @Composable
+    override fun elevation(interactionState: InteractionState): State<Dp> {
         val interaction = interactionState.value.lastOrNull {
             it is Interaction.Pressed
         }
@@ -261,21 +254,21 @@
             else -> defaultElevation
         }
 
-        val animatedElevation = lazyAnimatedElevation.animatedValueForTarget(target)
+        val animatable = remember { Animatable(target, Dp.VectorConverter) }
 
-        if (animatedElevation.targetValue != target) {
-            val lastInteraction = when (animatedElevation.targetValue) {
+        LaunchedEffect(target) {
+            val lastInteraction = when (animatable.targetValue) {
                 pressedElevation -> Interaction.Pressed
                 else -> null
             }
-            animatedElevation.animateElevation(
+            animatable.animateElevation(
                 from = lastInteraction,
                 to = interaction,
                 target = target
             )
         }
 
-        return animatedElevation.value
+        return animatable.asState()
     }
 }
 
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 e21c901..aeb0954 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
@@ -31,6 +31,8 @@
 import androidx.compose.ui.graphics.toolingGraphicsLayer
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 
 /**
@@ -38,6 +40,10 @@
  * clickable icon, see [IconButton].
  *
  * @param imageVector [ImageVector] to draw inside this Icon
+ * @param contentDescription text used by accessibility services to describe what this icon
+ * represents. This should always be provided unless this icon is used for decorative purposes,
+ * and does not represent a meaningful action that a user can take. This text should be
+ * localized, such as by using [androidx.compose.ui.res.stringResource] or similar
  * @param modifier optional [Modifier] for this Icon
  * @param tint tint to be applied to [imageVector]. If [Color.Unspecified] is provided, then no
  *  tint is applied
@@ -45,11 +51,13 @@
 @Composable
 fun Icon(
     imageVector: ImageVector,
+    contentDescription: String?,
     modifier: Modifier = Modifier,
     tint: Color = AmbientContentColor.current.copy(alpha = AmbientContentAlpha.current)
 ) {
     Icon(
         painter = rememberVectorPainter(imageVector),
+        contentDescription = contentDescription,
         modifier = modifier,
         tint = tint
     )
@@ -60,6 +68,10 @@
  * clickable icon, see [IconButton].
  *
  * @param bitmap [ImageBitmap] to draw inside this Icon
+ * @param contentDescription text used by accessibility services to describe what this icon
+ * represents. This should always be provided unless this icon is used for decorative purposes,
+ * and does not represent a meaningful action that a user can take. This text should be
+ * localized, such as by using [androidx.compose.ui.res.stringResource] or similar
  * @param modifier optional [Modifier] for this Icon
  * @param tint tint to be applied to [bitmap]. If [Color.Unspecified] is provided, then no
  *  tint is applied
@@ -67,12 +79,14 @@
 @Composable
 fun Icon(
     bitmap: ImageBitmap,
+    contentDescription: String?,
     modifier: Modifier = Modifier,
     tint: Color = AmbientContentColor.current
 ) {
     val painter = remember(bitmap) { ImagePainter(bitmap) }
     Icon(
         painter = painter,
+        contentDescription = contentDescription,
         modifier = modifier,
         tint = tint
     )
@@ -83,6 +97,10 @@
  * clickable icon, see [IconButton].
  *
  * @param painter [Painter] to draw inside this Icon
+ * @param contentDescription text used by accessibility services to describe what this icon
+ * represents. This should always be provided unless this icon is used for decorative purposes,
+ * and does not represent a meaningful action that a user can take. This text should be
+ * localized, such as by using [androidx.compose.ui.res.stringResource] or similar
  * @param modifier optional [Modifier] for this Icon
  * @param tint tint to be applied to [painter]. If [Color.Unspecified] is provided, then no
  *  tint is applied
@@ -90,6 +108,7 @@
 @Composable
 fun Icon(
     painter: Painter,
+    contentDescription: String?,
     modifier: Modifier = Modifier,
     tint: Color = AmbientContentColor.current.copy(alpha = AmbientContentAlpha.current)
 ) {
@@ -97,9 +116,15 @@
     // size that this icon will be forced to take up.
     // TODO: b/149735981 semantics for content description
     val colorFilter = if (tint == Color.Unspecified) null else ColorFilter.tint(tint)
+    val semantics = if (contentDescription != null) {
+        Modifier.semantics { this.contentDescription = contentDescription }
+    } else {
+        Modifier
+    }
     Box(
         modifier.toolingGraphicsLayer().defaultSizeFor(painter)
             .paint(painter, colorFilter = colorFilter)
+            .then(semantics)
     )
 }
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/LazyAnimatedValue.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/LazyAnimatedValue.kt
deleted file mode 100644
index 4c17027..0000000
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/LazyAnimatedValue.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material
-
-import androidx.compose.animation.core.AnimatedValue
-import androidx.compose.animation.core.AnimationVector
-
-// TODO: b/171041025 replace if/when similar functionality is added to the AnimatedValue APIs
-/**
- * A lazy wrapper around [AnimatedValue] that delays creating the [AnimatedValue] until the
- * initial value / target is known. This is similar to [androidx.compose.animation.animate], but
- * can be used outside of a Composable function.
- *
- * @property factory lazily invoked factory to create an [AnimatedValue] for the given target
- */
-internal class LazyAnimatedValue<T, V : AnimationVector>(
-    private val factory: (target: T) -> AnimatedValue<T, V>
-) {
-    private var animatedValue: AnimatedValue<T, V>? = null
-
-    /**
-     * @return a new [AnimatedValue] with an initial value equal to [targetValue], or the
-     * existing [AnimatedValue] if it has already been created.
-     */
-    fun animatedValueForTarget(targetValue: T): AnimatedValue<T, V> {
-        return animatedValue ?: factory(targetValue).also { animatedValue = it }
-    }
-}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
index 06df31a..2f919c026 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
@@ -19,7 +19,9 @@
 import androidx.compose.foundation.Interaction
 import androidx.compose.foundation.InteractionState
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.defaultMinSizeConstraints
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
@@ -70,8 +72,8 @@
  * be neither editable nor focusable, the input of the text field will not be selectable,
  * visually text field will appear in the disabled UI state
  * @param readOnly controls the editable state of the [OutlinedTextField]. When `true`, the text
- * fields will not be editable but otherwise operable. Read-only text fields are usually used to
- * display the pre-filled text that user cannot edit
+ * field can not be modified, however, a user can focus it and copy text from it. Read-only text
+ * fields are usually used to display pre-filled forms that user can not edit
  * @param textStyle the style to be applied to the input text. The default [textStyle] uses the
  * [AmbientTextStyle] defined by the theme
  * @param label the optional label to be displayed inside the text field container. The default
@@ -195,8 +197,8 @@
  * be neither editable nor focusable, the input of the text field will not be selectable,
  * visually text field will appear in the disabled UI state
  * @param readOnly controls the editable state of the [OutlinedTextField]. When `true`, the text
- * fields will not be editable but otherwise operable. Read-only text fields are usually used to
- * display the pre-filled text that user cannot edit
+ * field can not be modified, however, a user can focus it and copy text from it. Read-only text
+ * fields are usually used to display pre-filled forms that user can not edit
  * @param textStyle the style to be applied to the input text. The default [textStyle] uses the
  * [AmbientTextStyle] defined by the theme
  * @param label the optional label to be displayed inside the text field container. The default
@@ -295,18 +297,29 @@
 
 @Composable
 internal fun OutlinedTextFieldLayout(
-    modifier: Modifier = Modifier,
-    decoratedTextField: @Composable (Modifier) -> Unit,
+    modifier: Modifier,
+    value: TextFieldValue,
+    onValueChange: (TextFieldValue) -> Unit,
+    enabled: Boolean,
+    readOnly: Boolean,
+    keyboardOptions: KeyboardOptions,
+    textStyle: TextStyle,
+    singleLine: Boolean,
+    maxLines: Int = Int.MAX_VALUE,
+    onImeActionPerformed: (ImeAction) -> Unit = {},
+    visualTransformation: VisualTransformation,
+    onTextInputStarted: (SoftwareKeyboardController) -> Unit,
+    interactionState: InteractionState,
     decoratedPlaceholder: @Composable ((Modifier) -> Unit)?,
     decoratedLabel: @Composable (() -> Unit)?,
     leading: @Composable (() -> Unit)?,
     trailing: @Composable (() -> Unit)?,
-    singleLine: Boolean,
     leadingColor: Color,
     trailingColor: Color,
     labelProgress: Float,
     indicatorWidth: Dp,
-    indicatorColor: Color
+    indicatorColor: Color,
+    cursorColor: Color
 ) {
     val outlinedBorderParams = remember {
         OutlinedBorderParams(
@@ -321,24 +334,47 @@
         outlinedBorderParams.borderWidth.value = indicatorWidth
     }
 
-    // places leading icon, input field, label, placeholder, trailing icon
-    IconsWithTextFieldLayout(
-        modifier = modifier.drawOutlinedBorder(outlinedBorderParams),
-        textField = decoratedTextField,
-        leading = leading,
-        trailing = trailing,
+    BasicTextField(
+        value = value,
+        modifier = modifier
+            .defaultMinSizeConstraints(
+                minWidth = TextFieldMinWidth,
+                minHeight = TextFieldMinHeight + OutlinedTextFieldTopPadding,
+            )
+            .padding(top = OutlinedTextFieldTopPadding)
+            .drawOutlinedBorder(outlinedBorderParams),
+        >
+        enabled = enabled,
+        readOnly = readOnly,
+        textStyle = textStyle,
+        cursorColor = cursorColor,
+        visualTransformation = visualTransformation,
+        keyboardOptions = keyboardOptions,
+        interactionState = interactionState,
+        >
+        >
         singleLine = singleLine,
-        leadingColor = leadingColor,
-        trailingColor = trailingColor,
-        >
-            val labelWidth = it * labelProgress
-            if (outlinedBorderParams.labelWidth.value != labelWidth) {
-                outlinedBorderParams.labelWidth.value = labelWidth
-            }
-        },
-        animationProgress = labelProgress,
-        placeholder = decoratedPlaceholder,
-        label = decoratedLabel
+        maxLines = maxLines,
+        decorationBox = @Composable { coreTextField ->
+            // places leading icon, input field, label, placeholder, trailing icon
+            IconsWithTextFieldLayout(
+                textField = coreTextField,
+                leading = leading,
+                trailing = trailing,
+                singleLine = singleLine,
+                leadingColor = leadingColor,
+                trailingColor = trailingColor,
+                >
+                    val labelWidth = it * labelProgress
+                    if (outlinedBorderParams.labelWidth.value != labelWidth) {
+                        outlinedBorderParams.labelWidth.value = labelWidth
+                    }
+                },
+                animationProgress = labelProgress,
+                placeholder = decoratedPlaceholder,
+                label = decoratedLabel
+            )
+        }
     )
 }
 
@@ -350,8 +386,7 @@
 \ */
 @Composable
 private fun IconsWithTextFieldLayout(
-    modifier: Modifier = Modifier,
-    textField: @Composable (Modifier) -> Unit,
+    textField: @Composable () -> Unit,
     placeholder: @Composable ((Modifier) -> Unit)?,
     label: @Composable (() -> Unit)?,
     leading: @Composable (() -> Unit)?,
@@ -384,17 +419,14 @@
                 placeholder(Modifier.layoutId(PlaceholderId).padding(horizontal = TextFieldPadding))
             }
 
-            textField(
-                Modifier
-                    .layoutId(TextFieldId)
-                    .padding(horizontal = TextFieldPadding)
-            )
+            Box(Modifier.layoutId(TextFieldId).padding(horizontal = TextFieldPadding)) {
+                textField()
+            }
 
             if (label != null) {
                 Box(modifier = Modifier.layoutId(LabelId)) { label() }
             }
-        },
-        modifier = modifier
+        }
     ) { measurables, incomingConstraints ->
         // used to calculate the constraints for measuring elements that will be placed in a row
         var occupiedSpaceHorizontally = 0
@@ -682,4 +714,10 @@
 
 // TODO(b/158077409) support shape in OutlinedTextField
 private val OutlinedTextFieldCornerRadius = 4.dp
-private val OutlinedTextFieldInnerPadding = 4.dp
\ No newline at end of file
+private val OutlinedTextFieldInnerPadding = 4.dp
+/*
+This padding is used to allow label not overlap with the content above it. This 8.dp will work
+for default cases when developers do not override the label's font size. If they do, they will
+need to add additional padding themselves
+*/
+private val OutlinedTextFieldTopPadding = 8.dp
\ No newline at end of file
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/RadioButton.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/RadioButton.kt
index a8855a7..97bc87e 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/RadioButton.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/RadioButton.kt
@@ -16,12 +16,8 @@
 
 package androidx.compose.material
 
-import androidx.compose.animation.AnimatedValueModel
-import androidx.compose.animation.VectorConverter
-import androidx.compose.animation.animate
-import androidx.compose.animation.asDisposableClock
-import androidx.compose.animation.core.AnimationClockObservable
-import androidx.compose.animation.core.AnimationVector4D
+import androidx.compose.animation.animateAsState
+import androidx.compose.animation.core.animateAsState
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.Interaction
@@ -35,14 +31,16 @@
 import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.graphics.drawscope.Fill
 import androidx.compose.ui.graphics.drawscope.Stroke
-import androidx.compose.ui.platform.AmbientAnimationClock
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
@@ -79,11 +77,11 @@
     interactionState: InteractionState = remember { InteractionState() },
     colors: RadioButtonColors = RadioButtonDefaults.colors()
 ) {
-    @Suppress("Deprecation") // b/176192329
-    val dotRadius = animate(
-        target = if (selected) RadioButtonDotSize / 2 else 0.dp,
-        animSpec = tween(durationMillis = RadioAnimationDuration)
+    val dotRadius by animateAsState(
+        targetValue = if (selected) RadioButtonDotSize / 2 else 0.dp,
+        animationSpec = tween(durationMillis = RadioAnimationDuration)
     )
+    val radioColor by colors.radioColor(enabled, selected)
     Canvas(
         modifier
             .selectable(
@@ -101,7 +99,6 @@
             .padding(RadioButtonPadding)
             .size(RadioButtonSize)
     ) {
-        val radioColor = colors.radioColor(enabled, selected)
         drawRadio(radioColor, dotRadius)
     }
 }
@@ -122,7 +119,8 @@
      * @param enabled whether the [RadioButton] is enabled
      * @param selected whether the [RadioButton] is selected
      */
-    fun radioColor(enabled: Boolean, selected: Boolean): Color
+    @Composable
+    fun radioColor(enabled: Boolean, selected: Boolean): State<Color>
 }
 
 /**
@@ -145,14 +143,12 @@
         unselectedColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
         disabledColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
     ): RadioButtonColors {
-        val clock = AmbientAnimationClock.current.asDisposableClock()
         return remember(
             selectedColor,
             unselectedColor,
-            disabledColor,
-            clock
+            disabledColor
         ) {
-            DefaultRadioButtonColors(selectedColor, unselectedColor, disabledColor, clock)
+            DefaultRadioButtonColors(selectedColor, unselectedColor, disabledColor)
         }
     }
 }
@@ -173,14 +169,10 @@
 private class DefaultRadioButtonColors(
     private val selectedColor: Color,
     private val unselectedColor: Color,
-    private val disabledColor: Color,
-    private val clock: AnimationClockObservable
+    private val disabledColor: Color
 ) : RadioButtonColors {
-    private val lazyAnimatedColor = LazyAnimatedValue<Color, AnimationVector4D> { target ->
-        AnimatedValueModel(target, (Color.VectorConverter)(target.colorSpace), clock)
-    }
-
-    override fun radioColor(enabled: Boolean, selected: Boolean): Color {
+    @Composable
+    override fun radioColor(enabled: Boolean, selected: Boolean): State<Color> {
         val target = when {
             !enabled -> disabledColor
             !selected -> unselectedColor
@@ -190,14 +182,9 @@
         // If not enabled 'snap' to the disabled state, as there should be no animations between
         // enabled / disabled.
         return if (enabled) {
-            val animatedColor = lazyAnimatedColor.animatedValueForTarget(target)
-
-            if (animatedColor.targetValue != target) {
-                animatedColor.animateTo(target, tween(durationMillis = RadioAnimationDuration))
-            }
-            animatedColor.value
+            animateAsState(target, tween(durationMillis = RadioAnimationDuration))
         } else {
-            target
+            rememberUpdatedState(target)
         }
     }
 }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
index 00f6b24..8d9a5a9 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
@@ -47,7 +47,6 @@
  * inside of the [Scaffold]
  */
 @Stable
-@OptIn(ExperimentalMaterialApi::class)
 class ScaffoldState(
     val drawerState: DrawerState,
     val snackbarHostState: SnackbarHostState
@@ -61,7 +60,6 @@
  * inside of the [Scaffold]
  */
 @Composable
-@OptIn(ExperimentalMaterialApi::class)
 fun rememberScaffoldState(
     drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
     snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }
@@ -147,7 +145,6 @@
  * the scroller itself.
  */
 @Composable
-@OptIn(ExperimentalMaterialApi::class)
 fun Scaffold(
     modifier: Modifier = Modifier,
     scaffoldState: ScaffoldState = rememberScaffoldState(),
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Snackbar.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Snackbar.kt
index 256a30b..210ae2a 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Snackbar.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Snackbar.kt
@@ -141,7 +141,6 @@
  * of the shadow below the SnackBar
  */
 @Composable
-@OptIn(ExperimentalMaterialApi::class)
 fun Snackbar(
     snackbarData: SnackbarData,
     modifier: Modifier = Modifier,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SnackbarHost.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SnackbarHost.kt
index 63e9548..5bb7010 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SnackbarHost.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SnackbarHost.kt
@@ -53,7 +53,6 @@
  * automatically, but can be decoupled from it and live separately when desired.
  */
 @Stable
-@ExperimentalMaterialApi
 class SnackbarHostState {
 
     /**
@@ -107,7 +106,6 @@
     }
 
     @Stable
-    @OptIn(ExperimentalMaterialApi::class)
     private class SnackbarDataImpl(
         override val message: String,
         override val actionLabel: String?,
@@ -146,7 +144,6 @@
  * appearance based on the [SnackbarData] provided as a param
  */
 @Composable
-@ExperimentalMaterialApi
 fun SnackbarHost(
     hostState: SnackbarHostState,
     modifier: Modifier = Modifier,
@@ -173,7 +170,6 @@
  * @property actionLabel optional action label to show as button in the Snackbar
  * @property duration duration of the snackbar
  */
-@ExperimentalMaterialApi
 interface SnackbarData {
     val message: String
     val actionLabel: String?
@@ -235,7 +231,6 @@
 // TODO: to be replaced with the public customizable implementation
 // it's basically tweaked nullable version of Crossfade
 @Composable
-@OptIn(ExperimentalMaterialApi::class)
 private fun FadeInFadeOutWithScale(
     current: SnackbarData?,
     modifier: Modifier = Modifier,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
index d0d8f9c..ec65b93 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
@@ -36,7 +36,9 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.emptyContent
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
@@ -134,7 +136,8 @@
      * @param enabled whether the [Switch] is enabled or not
      * @param checked whether the [Switch] is checked or not
      */
-    fun thumbColor(enabled: Boolean, checked: Boolean): Color
+    @Composable
+    fun thumbColor(enabled: Boolean, checked: Boolean): State<Color>
 
     /**
      * Represents the color used for the switch's track, depending on [enabled] and [checked].
@@ -142,7 +145,8 @@
      * @param enabled whether the [Switch] is enabled or not
      * @param checked whether the [Switch] is checked or not
      */
-    fun trackColor(enabled: Boolean, checked: Boolean): Color
+    @Composable
+    fun trackColor(enabled: Boolean, checked: Boolean): State<Color>
 }
 
 @OptIn(ExperimentalMaterialApi::class)
@@ -161,11 +165,11 @@
     } else {
         ThumbDefaultElevation
     }
+    val trackColor by colors.trackColor(enabled, checked)
     Canvas(Modifier.align(Alignment.Center).fillMaxSize()) {
-        val trackColor = colors.trackColor(enabled, checked)
         drawTrack(trackColor, TrackWidth.toPx(), TrackStrokeWidth.toPx())
     }
-    val thumbColor = colors.thumbColor(enabled, checked)
+    val thumbColor by colors.thumbColor(enabled, checked)
     Surface(
         shape = CircleShape,
         color = thumbColor,
@@ -278,20 +282,26 @@
     private val disabledUncheckedThumbColor: Color,
     private val disabledUncheckedTrackColor: Color
 ) : SwitchColors {
-    override fun thumbColor(enabled: Boolean, checked: Boolean): Color {
-        return if (enabled) {
-            if (checked) checkedThumbColor else uncheckedThumbColor
-        } else {
-            if (checked) disabledCheckedThumbColor else disabledUncheckedThumbColor
-        }
+    @Composable
+    override fun thumbColor(enabled: Boolean, checked: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) {
+                if (checked) checkedThumbColor else uncheckedThumbColor
+            } else {
+                if (checked) disabledCheckedThumbColor else disabledUncheckedThumbColor
+            }
+        )
     }
 
-    override fun trackColor(enabled: Boolean, checked: Boolean): Color {
-        return if (enabled) {
-            if (checked) checkedTrackColor else uncheckedTrackColor
-        } else {
-            if (checked) disabledCheckedTrackColor else disabledUncheckedTrackColor
-        }
+    @Composable
+    override fun trackColor(enabled: Boolean, checked: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) {
+                if (checked) checkedTrackColor else uncheckedTrackColor
+            } else {
+                if (checked) disabledCheckedTrackColor else disabledUncheckedTrackColor
+            }
+        )
     }
 
     override fun equals(other: Any?): Boolean {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
index dc4b069..6f2106a 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
@@ -20,8 +20,10 @@
 import androidx.compose.foundation.InteractionState
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.defaultMinSizeConstraints
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.shape.ZeroCornerSize
+import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
@@ -96,9 +98,9 @@
  * @param enabled controls the enabled state of the [TextField]. When `false`, the text field will
  * be neither editable nor focusable, the input of the text field will not be selectable,
  * visually text field will appear in the disabled UI state
- * @param readOnly controls the editable state of the [TextField]. When `true`, the text fields
- * will not be editable but otherwise operable. Read-only text fields are usually used to display
- * the pre-filled text that user cannot edit
+ * @param readOnly controls the editable state of the [TextField]. When `true`, the text
+ * field can not be modified, however, a user can focus it and copy text from it. Read-only text
+ * fields are usually used to display pre-filled forms that user can not edit
  * @param textStyle the style to be applied to the input text. The default [textStyle] uses the
  * [AmbientTextStyle] defined by the theme
  * @param label the optional label to be displayed inside the text field container. The default
@@ -228,9 +230,9 @@
  * @param enabled controls the enabled state of the [TextField]. When `false`, the text field will
  * be neither editable nor focusable, the input of the text field will not be selectable,
  * visually text field will appear in the disabled UI state
- * @param readOnly controls the editable state of the [TextField]. When `true`, the text fields
- * will not be editable but otherwise operable. Read-only text fields are usually used to display
- * the pre-filled text that user cannot edit
+ * @param readOnly controls the editable state of the [TextField]. When `true`, the text
+ * field can not be modified, however, a user can focus it and copy text from it. Read-only text
+ * fields are usually used to display pre-filled forms that user can not edit
  * @param textStyle the style to be applied to the input text. The default [textStyle] uses the
  * [AmbientTextStyle] defined by the theme
  * @param label the optional label to be displayed inside the text field container. The default
@@ -334,41 +336,67 @@
 
 @Composable
 internal fun TextFieldLayout(
-    modifier: Modifier = Modifier,
-    decoratedTextField: @Composable (Modifier) -> Unit,
+    modifier: Modifier,
+    value: TextFieldValue,
+    onValueChange: (TextFieldValue) -> Unit,
+    enabled: Boolean,
+    readOnly: Boolean,
+    keyboardOptions: KeyboardOptions,
+    textStyle: TextStyle,
+    singleLine: Boolean,
+    maxLines: Int = Int.MAX_VALUE,
+    onImeActionPerformed: (ImeAction) -> Unit = {},
+    visualTransformation: VisualTransformation,
+    onTextInputStarted: (SoftwareKeyboardController) -> Unit,
+    interactionState: InteractionState,
     decoratedPlaceholder: @Composable ((Modifier) -> Unit)?,
     decoratedLabel: @Composable (() -> Unit)?,
     leading: @Composable (() -> Unit)?,
     trailing: @Composable (() -> Unit)?,
-    singleLine: Boolean,
     leadingColor: Color,
     trailingColor: Color,
     labelProgress: Float,
     indicatorWidth: Dp,
     indicatorColor: Color,
     backgroundColor: Color,
+    cursorColor: Color,
     shape: Shape
 ) {
-    // places leading icon, text field with label and placeholder, trailing icon
-    IconsWithTextFieldLayout(
+    BasicTextField(
+        value = value,
         modifier = modifier
-            .background(
-                color = backgroundColor,
-                shape = shape
+            .defaultMinSizeConstraints(
+                minWidth = TextFieldMinWidth,
+                minHeight = TextFieldMinHeight
             )
-            .drawIndicatorLine(
-                lineWidth = indicatorWidth,
-                color = indicatorColor
-            ),
-        textField = decoratedTextField,
-        placeholder = decoratedPlaceholder,
-        label = decoratedLabel,
-        leading = leading,
-        trailing = trailing,
+            .background(color = backgroundColor, shape = shape)
+            .drawIndicatorLine(lineWidth = indicatorWidth, color = indicatorColor),
+        >
+        enabled = enabled,
+        readOnly = readOnly,
+        textStyle = textStyle,
+        cursorColor = cursorColor,
+        visualTransformation = visualTransformation,
+        keyboardOptions = keyboardOptions,
+        interactionState = interactionState,
+        >
+        >
         singleLine = singleLine,
-        leadingColor = leadingColor,
-        trailingColor = trailingColor,
-        animationProgress = labelProgress
+        maxLines = maxLines,
+        decorationBox = @Composable { coreTextField ->
+            // places leading icon, text field with label and placeholder, trailing icon
+            IconsWithTextFieldLayout(
+                textField = coreTextField,
+                placeholder = decoratedPlaceholder,
+                label = decoratedLabel,
+                leading = leading,
+                trailing = trailing,
+                singleLine = singleLine,
+                leadingColor = leadingColor,
+                trailingColor = trailingColor,
+                animationProgress = labelProgress
+            )
+        }
     )
 }
 
@@ -378,8 +406,7 @@
  */
 @Composable
 private fun IconsWithTextFieldLayout(
-    modifier: Modifier = Modifier,
-    textField: @Composable (Modifier) -> Unit,
+    textField: @Composable () -> Unit,
     label: @Composable (() -> Unit)?,
     placeholder: @Composable ((Modifier) -> Unit)?,
     leading: @Composable (() -> Unit)?,
@@ -421,9 +448,8 @@
                         )
                 ) { label() }
             }
-            textField(Modifier.layoutId(TextFieldId).then(padding))
-        },
-        modifier = modifier
+            Box(Modifier.layoutId(TextFieldId).then(padding)) { textField() }
+        }
     ) { measurables, incomingConstraints ->
         val topBottomPadding = TextFieldPadding.toIntPx()
         val baseLineOffset = FirstBaselineOffset.toIntPx()
@@ -681,7 +707,7 @@
 /**
  * A draw modifier that draws a bottom indicator line in [TextField]
  */
-private fun Modifier.drawIndicatorLine(lineWidth: Dp, color: Color): Modifier {
+internal fun Modifier.drawIndicatorLine(lineWidth: Dp, color: Color): Modifier {
     return drawBehind {
         val strokeWidth = lineWidth.value * density
         val y = size.height - strokeWidth / 2
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 76257df..4dc2446 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
@@ -26,12 +26,7 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Interaction
 import androidx.compose.foundation.InteractionState
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.defaultMinSizeConstraints
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.preferredSizeIn
-import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
@@ -39,8 +34,6 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.alpha
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.takeOrElse
@@ -52,6 +45,7 @@
 import androidx.compose.ui.node.Ref
 import androidx.compose.ui.platform.InspectorValueInfo
 import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.SoftwareKeyboardController
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
@@ -72,7 +66,7 @@
  * Implementation of the [TextField] and [OutlinedTextField]
  */
 @Composable
-@OptIn(ExperimentalFoundationApi::class)
+@OptIn(ExperimentalFoundationApi::class, InternalTextApi::class)
 internal fun TextFieldImpl(
     type: TextFieldType,
     enabled: Boolean,
@@ -115,55 +109,6 @@
         else -> InputPhase.UnfocusedNotEmpty
     }
 
-    val decoratedTextField: @Composable (Modifier) -> Unit = @Composable { tagModifier ->
-        Decoration(
-            contentColor = inactiveColor,
-            typography = MaterialTheme.typography.subtitle1,
-            contentAlpha = if (enabled) ContentAlpha.high else ContentAlpha.disabled
-        ) {
-            BasicTextField(
-                value = value,
-                modifier = tagModifier.defaultMinSizeConstraints(minWidth = TextFieldMinWidth),
-                textStyle = mergedTextStyle,
-                enabled = enabled,
-                readOnly = readOnly,
-                >
-                cursorColor = if (isErrorValue) errorColor else activeColor,
-                visualTransformation = visualTransformation,
-                keyboardOptions = keyboardOptions,
-                maxLines = maxLines,
-                interactionState = interactionState,
-                >
-                    onImeActionPerformed(it, keyboardController.value)
-                },
-                >
-                    keyboardController.value = it
-                    onTextInputStarted(it)
-                },
-                singleLine = singleLine
-            )
-        }
-    }
-
-    val focusRequester = FocusRequester()
-    val textFieldModifier = if (enabled) {
-        modifier
-            .focusRequester(focusRequester)
-            .clickable(interactionState = interactionState, indication = null) {
-                focusRequester.requestFocus()
-                // TODO(b/163109449): Showing and hiding keyboard should be handled by BaseTextField.
-                //  The requestFocus() call here should be enough to trigger the software keyboard.
-                //  Investiate why this is needed here. If it is really needed, instead of doing
-                //  this in the onClick callback, we should move this logic to onFocusChanged
-                //  so that it can show or hide the keyboard based on the focus state.
-                if (!readOnly) {
-                    keyboardController.value?.showSoftwareKeyboard()
-                }
-            }
-    } else {
-        modifier
-    }
-
     TextFieldTransitionScope.Transition(
         inputState = inputState,
         showLabel = label != null,
@@ -221,50 +166,69 @@
                 }
             } else null
 
+        val cursorColor = if (isErrorValue) errorColor else activeColor
+        val onImeActionPerformedAction: (ImeAction) -> Unit = {
+            onImeActionPerformed(it, keyboardController.value)
+        }
+        val onTextInputStartedAction: (SoftwareKeyboardController) -> Unit = {
+            keyboardController.value = it
+            onTextInputStarted(it)
+        }
         when (type) {
             TextFieldType.Filled -> {
                 TextFieldLayout(
-                    modifier = Modifier
-                        .preferredSizeIn(
-                            minWidth = TextFieldMinWidth,
-                            minHeight = TextFieldMinHeight
-                        )
-                        .then(textFieldModifier),
-                    decoratedTextField = decoratedTextField,
+                    modifier = modifier,
+                    value = value,
+                    >
+                    enabled = enabled,
+                    readOnly = readOnly,
+                    keyboardOptions = keyboardOptions,
+                    textStyle = mergedTextStyle,
+                    singleLine = singleLine,
+                    maxLines = maxLines,
+                    >
+                    visualTransformation = visualTransformation,
+                    >
+                    interactionState = interactionState,
                     decoratedPlaceholder = decoratedPlaceholder,
                     decoratedLabel = decoratedLabel,
                     leading = leading,
                     trailing = trailing,
-                    singleLine = singleLine,
                     leadingColor = leadingColor,
                     trailingColor = trailingColor,
                     labelProgress = labelProgress,
                     indicatorWidth = indicatorWidth,
                     indicatorColor = indicatorColor,
                     backgroundColor = backgroundColor,
+                    cursorColor = cursorColor,
                     shape = shape
                 )
             }
             TextFieldType.Outlined -> {
                 OutlinedTextFieldLayout(
-                    modifier = Modifier
-                        .preferredSizeIn(
-                            minWidth = TextFieldMinWidth,
-                            minHeight = TextFieldMinHeight + OutlinedTextFieldTopPadding
-                        )
-                        .then(textFieldModifier)
-                        .padding(top = OutlinedTextFieldTopPadding),
-                    decoratedTextField = decoratedTextField,
+                    modifier = modifier,
+                    value = value,
+                    >
+                    enabled = enabled,
+                    readOnly = readOnly,
+                    keyboardOptions = keyboardOptions,
+                    textStyle = mergedTextStyle,
+                    singleLine = singleLine,
+                    maxLines = maxLines,
+                    >
+                    visualTransformation = visualTransformation,
+                    >
+                    interactionState = interactionState,
                     decoratedPlaceholder = decoratedPlaceholder,
                     decoratedLabel = decoratedLabel,
                     leading = leading,
                     trailing = trailing,
-                    singleLine = singleLine,
                     leadingColor = leadingColor,
                     trailingColor = trailingColor,
                     labelProgress = labelProgress,
                     indicatorWidth = indicatorWidth,
-                    indicatorColor = indicatorColor
+                    indicatorColor = indicatorColor,
+                    cursorColor = cursorColor
                 )
             }
         }
@@ -461,17 +425,10 @@
 private val IndicatorUnfocusedWidth = 1.dp
 private val IndicatorFocusedWidth = 2.dp
 private const val TrailingLeadingAlpha = 0.54f
-private val TextFieldMinHeight = 56.dp
-private val TextFieldMinWidth = 280.dp
+internal val TextFieldMinHeight = 56.dp
+internal val TextFieldMinWidth = 280.dp
 internal val TextFieldPadding = 16.dp
 internal val HorizontalIconPadding = 12.dp
 
 // Filled text field uses 42% opacity to meet the contrast requirements for accessibility reasons
 private const val IndicatorInactiveAlpha = 0.42f
-
-/*
-This padding is used to allow label not overlap with the content above it. This 8.dp will work
-for default cases when developers do not override the label's font size. If they do, they will
-need to add additional padding themselves
-*/
-private val OutlinedTextFieldTopPadding = 8.dp
diff --git a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/benchmark/BenchmarksExtensions.kt b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/benchmark/BenchmarksExtensions.kt
index f32860a..c53a78e 100644
--- a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/benchmark/BenchmarksExtensions.kt
+++ b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/benchmark/BenchmarksExtensions.kt
@@ -147,8 +147,16 @@
 
 /**
  * Measures the time of the first composition of the given compose test case.
+ *
+ * @param assertNoPendingRecompositions whether the benchmark will fail if there are pending
+ * recompositions after the first composition. By default this is true to enforce correctness in
+ * the benchmark, but for components that have an initial animation after being composed this can
+ * be turned off to benchmark just the first composition without any pending animations.
  */
-fun ComposeBenchmarkRule.benchmarkFirstCompose(caseFactory: () -> ComposeTestCase) {
+fun ComposeBenchmarkRule.benchmarkFirstCompose(
+    caseFactory: () -> ComposeTestCase,
+    assertNoPendingRecompositions: Boolean = true
+) {
     runBenchmarkFor(caseFactory) {
         measureRepeated {
             runWithTimingDisabled {
@@ -158,7 +166,9 @@
             emitContent()
 
             runWithTimingDisabled {
-                assertNoPendingChanges()
+                if (assertNoPendingRecompositions) {
+                    assertNoPendingChanges()
+                }
                 disposeContent()
             }
         }
@@ -311,9 +321,15 @@
 
 /**
  *  Measures recomposition time of the hierarchy after changing a state.
+ *
+ * @param assertOneRecomposition whether the benchmark will fail if there are pending
+ * recompositions after the first recomposition. By default this is true to enforce correctness in
+ * the benchmark, but for components that have animations after being recomposed this can
+ * be turned off to benchmark just the first recomposition without any pending animations.
  */
 fun <T> ComposeBenchmarkRule.toggleStateBenchmarkRecompose(
-    caseFactory: () -> T
+    caseFactory: () -> T,
+    assertOneRecomposition: Boolean = true
 ) where T : ComposeTestCase, T : ToggleableTestCase {
     runBenchmarkFor(caseFactory) {
         doFramesUntilNoChangesPending()
@@ -323,17 +339,25 @@
                 getTestCase().toggleState()
             }
             recomposeAssertHadChanges()
-            assertNoPendingChanges()
+            if (assertOneRecomposition) {
+                assertNoPendingChanges()
+            }
         }
     }
 }
 
 /**
  *  Measures measure time of the hierarchy after changing a state.
+ *
+ * @param assertOneRecomposition whether the benchmark will fail if there are pending
+ * recompositions after the first recomposition. By default this is true to enforce correctness in
+ * the benchmark, but for components that have animations after being recomposed this can
+ * be turned off to benchmark just the first remeasure without any pending animations.
  */
 fun <T> ComposeBenchmarkRule.toggleStateBenchmarkMeasure(
     caseFactory: () -> T,
-    toggleCausesRecompose: Boolean = true
+    toggleCausesRecompose: Boolean = true,
+    assertOneRecomposition: Boolean = true
 ) where T : ComposeTestCase, T : ToggleableTestCase {
     runBenchmarkFor(caseFactory) {
         doFramesUntilNoChangesPending()
@@ -345,20 +369,30 @@
                     recomposeAssertHadChanges()
                 }
                 requestLayout()
-                assertNoPendingChanges()
+                if (assertOneRecomposition) {
+                    assertNoPendingChanges()
+                }
             }
             measure()
-            assertNoPendingChanges()
+            if (assertOneRecomposition) {
+                assertNoPendingChanges()
+            }
         }
     }
 }
 
 /**
  *  Measures layout time of the hierarchy after changing a state.
+ *
+ * @param assertOneRecomposition whether the benchmark will fail if there are pending
+ * recompositions after the first recomposition. By default this is true to enforce correctness in
+ * the benchmark, but for components that have animations after being recomposed this can
+ * be turned off to benchmark just the first relayout without any pending animations.
  */
 fun <T> ComposeBenchmarkRule.toggleStateBenchmarkLayout(
     caseFactory: () -> T,
-    toggleCausesRecompose: Boolean = true
+    toggleCausesRecompose: Boolean = true,
+    assertOneRecomposition: Boolean = true
 ) where T : ComposeTestCase, T : ToggleableTestCase {
     runBenchmarkFor(caseFactory) {
         doFramesUntilNoChangesPending()
@@ -371,20 +405,30 @@
                 }
                 requestLayout()
                 measure()
-                assertNoPendingChanges()
+                if (assertOneRecomposition) {
+                    assertNoPendingChanges()
+                }
             }
             layout()
-            assertNoPendingChanges()
+            if (assertOneRecomposition) {
+                assertNoPendingChanges()
+            }
         }
     }
 }
 
 /**
  *  Measures draw time of the hierarchy after changing a state.
+ *
+ * @param assertOneRecomposition whether the benchmark will fail if there are pending
+ * recompositions after the first recomposition. By default this is true to enforce correctness in
+ * the benchmark, but for components that have animations after being recomposed this can
+ * be turned off to benchmark just the first redraw without any pending animations.
  */
 fun <T> ComposeBenchmarkRule.toggleStateBenchmarkDraw(
     caseFactory: () -> T,
-    toggleCausesRecompose: Boolean = true
+    toggleCausesRecompose: Boolean = true,
+    assertOneRecomposition: Boolean = true
 ) where T : ComposeTestCase, T : ToggleableTestCase {
     runBenchmarkFor(caseFactory) {
         doFramesUntilNoChangesPending()
@@ -395,7 +439,9 @@
                 if (toggleCausesRecompose) {
                     recomposeAssertHadChanges()
                 }
-                assertNoPendingChanges()
+                if (assertOneRecomposition) {
+                    assertNoPendingChanges()
+                }
                 requestLayout()
                 measure()
                 layout()
@@ -472,9 +518,16 @@
 
 /**
  *  Measures recompose, measure and layout time after changing a state.
+ *
+ * @param assertOneRecomposition whether the benchmark will fail if there are pending
+ * recompositions after the first recomposition. By default this is true to enforce correctness in
+ * the benchmark, but for components that have animations after being recomposed this can
+ * be turned off to benchmark just the first recompose, remeasure and relayout without any pending
+ * animations.
  */
 fun <T> ComposeBenchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
-    caseFactory: () -> T
+    caseFactory: () -> T,
+    assertOneRecomposition: Boolean = true
 ) where T : ComposeTestCase, T : ToggleableTestCase {
     runBenchmarkFor(caseFactory) {
         doFramesUntilNoChangesPending()
@@ -482,7 +535,9 @@
         measureRepeated {
             getTestCase().toggleState()
             recomposeAssertHadChanges()
-            assertNoPendingChanges()
+            if (assertOneRecomposition) {
+                assertNoPendingChanges()
+            }
             measure()
             layout()
             runWithTimingDisabled {
@@ -496,9 +551,15 @@
 
 /**
  *  Measures measure and layout time after changing a state.
+ *
+ * @param assertOneRecomposition whether the benchmark will fail if there are pending
+ * recompositions after the first recomposition. By default this is true to enforce correctness in
+ * the benchmark, but for components that have animations after being recomposed this can
+ * be turned off to benchmark just the first remeasure and relayout without any pending animations.
  */
 fun <T> ComposeBenchmarkRule.toggleStateBenchmarkMeasureLayout(
-    caseFactory: () -> T
+    caseFactory: () -> T,
+    assertOneRecomposition: Boolean = true
 ) where T : ComposeTestCase, T : ToggleableTestCase {
     runBenchmarkFor(caseFactory) {
         doFramesUntilNoChangesPending()
@@ -506,10 +567,14 @@
         measureRepeated {
             runWithTimingDisabled {
                 getTestCase().toggleState()
-                assertNoPendingChanges()
+                if (assertOneRecomposition) {
+                    assertNoPendingChanges()
+                }
             }
             measure()
-            assertNoPendingChanges()
+            if (assertOneRecomposition) {
+                assertNoPendingChanges()
+            }
         }
     }
 }
diff --git a/compose/ui/ui-inspection/build.gradle b/compose/ui/ui-inspection/build.gradle
new file mode 100644
index 0000000..9c4b9f5
--- /dev/null
+++ b/compose/ui/ui-inspection/build.gradle
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryType
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+    id("androidx.inspection")
+}
+
+dependencies {
+    implementation("androidx.annotation:annotation:1.1.0")
+    implementation(KOTLIN_STDLIB)
+    compileOnly(projectOrArtifact(":inspection:inspection"))
+    compileOnly(project(":compose:runtime:runtime"))
+    compileOnly(project(":compose:ui:ui"))
+    compileOnly(project(":compose:ui:ui-tooling"))
+}
+
+androidx {
+    name = "Android Compose Layout Inspector"
+    type = LibraryType.IDE_PLUGIN
+    mavenGroup = LibraryGroups.Compose.UI
+    inceptionYear = "2021"
+    description = "Compose layout inspector. Exposes information to our tools for better IDE support."
+}
+
+android {
+    defaultConfig {
+        // layout inspection supported starting on Android Q
+        minSdkVersion 29
+    }
+
+    sourceSets {
+        main.resources.srcDirs += "src/main/proto"
+    }
+}
+
+tasks.withType(KotlinCompile).configureEach {
+    kotlinOptions {
+        useIR = true
+        freeCompilerArgs += [
+                "-Xopt-in=kotlin.RequiresOptIn",
+                "-P", "plugin:androidx.compose.compiler.plugins.kotlin:sourceInformation=true"
+        ]
+    }
+}
diff --git a/compose/ui/ui-inspection/lint-baseline.xml b/compose/ui/ui-inspection/lint-baseline.xml
new file mode 100644
index 0000000..8f1aa4b
--- /dev/null
+++ b/compose/ui/ui-inspection/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.2.0-beta02" client="gradle" variant="debug" version="4.2.0-beta02">
+
+</issues>
diff --git a/compose/ui/ui-inspection/src/main/AndroidManifest.xml b/compose/ui/ui-inspection/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5f27d96
--- /dev/null
+++ b/compose/ui/ui-inspection/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest package="androidx.compose.ui.inspection" />
\ No newline at end of file
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt
new file mode 100644
index 0000000..133e045
--- /dev/null
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.inspection
+
+import androidx.inspection.Connection
+import androidx.inspection.Inspector
+import androidx.inspection.InspectorEnvironment
+import androidx.inspection.InspectorFactory
+import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol
+
+private const val LAYOUT_INSPECTION_ID = "layoutinspector.compose.inspection"
+
+// created by java.util.ServiceLoader
+class ComposeLayoutInspectorFactory :
+    InspectorFactory<ComposeLayoutInspector>(LAYOUT_INSPECTION_ID) {
+    override fun createInspector(
+        connection: Connection,
+        environment: InspectorEnvironment
+    ): ComposeLayoutInspector {
+        return ComposeLayoutInspector(connection)
+    }
+}
+
+class ComposeLayoutInspector(
+    connection: Connection,
+) : Inspector(connection) {
+
+    override fun onReceiveCommand(data: ByteArray, callback: CommandCallback) {
+        // TODO: Actually reply with a real response
+        callback.reply(LayoutInspectorComposeProtocol.Response.getDefaultInstance().toByteArray())
+    }
+}
\ No newline at end of file
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.java b/compose/ui/ui-inspection/src/main/proto/compose_layout_inspection.proto
similarity index 60%
copy from navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.java
copy to compose/ui/ui-inspection/src/main/proto/compose_layout_inspection.proto
index f9f0fbb..c6df549 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.java
+++ b/compose/ui/ui-inspection/src/main/proto/compose_layout_inspection.proto
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,11 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+syntax = "proto3";
+package layoutinspector.compose.inspection;
+option java_package = "layoutinspector.compose.inspection";
+option java_outer_classname = "LayoutInspectorComposeProtocol";
 
-package androidx.navigation;
+// ======= MESSAGES =======
 
-/**
- * An interface marking generated Args classes.
- */
-public interface NavArgs {
+// ======= COMMANDS, RESPONSES, AND EVENTS =======
+
+message Command {
+}
+
+message Response {
 }
diff --git a/compose/ui/ui-inspection/src/main/resources/META-INF/services/androidx.inspection.InspectorFactory b/compose/ui/ui-inspection/src/main/resources/META-INF/services/androidx.inspection.InspectorFactory
new file mode 100644
index 0000000..e13fa9c
--- /dev/null
+++ b/compose/ui/ui-inspection/src/main/resources/META-INF/services/androidx.inspection.InspectorFactory
@@ -0,0 +1 @@
+androidx.compose.ui.inspection.ComposeLayoutInspectorFactory
diff --git a/compose/ui/ui-test-junit4/api/current.txt b/compose/ui/ui-test-junit4/api/current.txt
index 04f1c78..614612d 100644
--- a/compose/ui/ui-test-junit4/api/current.txt
+++ b/compose/ui/ui-test-junit4/api/current.txt
@@ -12,7 +12,7 @@
     method public R getActivityRule();
     method public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
     method public androidx.compose.ui.unit.Density getDensity();
-    method public long getDisplaySize-YbymL2g();
+    method @Deprecated public long getDisplaySize-YbymL2g();
     method public androidx.compose.ui.test.MainTestClock getMainClock();
     method public androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodes(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
     method public androidx.compose.ui.test.SemanticsNodeInteraction onNode(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
@@ -26,7 +26,7 @@
     property public final R activityRule;
     property public androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
     property public androidx.compose.ui.unit.Density density;
-    property public long displaySize;
+    property @Deprecated public long displaySize;
     property public androidx.compose.ui.test.MainTestClock mainClock;
   }
 
@@ -61,7 +61,7 @@
     method @androidx.compose.ui.test.ExperimentalTestApi public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method @Deprecated public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
     method public androidx.compose.ui.unit.Density getDensity();
-    method public long getDisplaySize-YbymL2g();
+    method @Deprecated public long getDisplaySize-YbymL2g();
     method public androidx.compose.ui.test.MainTestClock getMainClock();
     method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public <T> T! runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
@@ -72,7 +72,7 @@
     method public void waitUntil(optional long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
     property @Deprecated public abstract androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
     property public abstract androidx.compose.ui.unit.Density density;
-    property public abstract long displaySize;
+    property @Deprecated public abstract long displaySize;
     property public abstract androidx.compose.ui.test.MainTestClock mainClock;
   }
 
diff --git a/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt b/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt
index 04f1c78..614612d 100644
--- a/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt
@@ -12,7 +12,7 @@
     method public R getActivityRule();
     method public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
     method public androidx.compose.ui.unit.Density getDensity();
-    method public long getDisplaySize-YbymL2g();
+    method @Deprecated public long getDisplaySize-YbymL2g();
     method public androidx.compose.ui.test.MainTestClock getMainClock();
     method public androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodes(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
     method public androidx.compose.ui.test.SemanticsNodeInteraction onNode(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
@@ -26,7 +26,7 @@
     property public final R activityRule;
     property public androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
     property public androidx.compose.ui.unit.Density density;
-    property public long displaySize;
+    property @Deprecated public long displaySize;
     property public androidx.compose.ui.test.MainTestClock mainClock;
   }
 
@@ -61,7 +61,7 @@
     method @androidx.compose.ui.test.ExperimentalTestApi public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method @Deprecated public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
     method public androidx.compose.ui.unit.Density getDensity();
-    method public long getDisplaySize-YbymL2g();
+    method @Deprecated public long getDisplaySize-YbymL2g();
     method public androidx.compose.ui.test.MainTestClock getMainClock();
     method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public <T> T! runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
@@ -72,7 +72,7 @@
     method public void waitUntil(optional long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
     property @Deprecated public abstract androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
     property public abstract androidx.compose.ui.unit.Density density;
-    property public abstract long displaySize;
+    property @Deprecated public abstract long displaySize;
     property public abstract androidx.compose.ui.test.MainTestClock mainClock;
   }
 
diff --git a/compose/ui/ui-test-junit4/api/restricted_current.txt b/compose/ui/ui-test-junit4/api/restricted_current.txt
index 04f1c78..614612d 100644
--- a/compose/ui/ui-test-junit4/api/restricted_current.txt
+++ b/compose/ui/ui-test-junit4/api/restricted_current.txt
@@ -12,7 +12,7 @@
     method public R getActivityRule();
     method public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
     method public androidx.compose.ui.unit.Density getDensity();
-    method public long getDisplaySize-YbymL2g();
+    method @Deprecated public long getDisplaySize-YbymL2g();
     method public androidx.compose.ui.test.MainTestClock getMainClock();
     method public androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodes(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
     method public androidx.compose.ui.test.SemanticsNodeInteraction onNode(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
@@ -26,7 +26,7 @@
     property public final R activityRule;
     property public androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
     property public androidx.compose.ui.unit.Density density;
-    property public long displaySize;
+    property @Deprecated public long displaySize;
     property public androidx.compose.ui.test.MainTestClock mainClock;
   }
 
@@ -61,7 +61,7 @@
     method @androidx.compose.ui.test.ExperimentalTestApi public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method @Deprecated public androidx.compose.ui.test.junit4.AnimationClockTestRule getClockTestRule();
     method public androidx.compose.ui.unit.Density getDensity();
-    method public long getDisplaySize-YbymL2g();
+    method @Deprecated public long getDisplaySize-YbymL2g();
     method public androidx.compose.ui.test.MainTestClock getMainClock();
     method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
     method public <T> T! runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
@@ -72,7 +72,7 @@
     method public void waitUntil(optional long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
     property @Deprecated public abstract androidx.compose.ui.test.junit4.AnimationClockTestRule clockTestRule;
     property public abstract androidx.compose.ui.unit.Density density;
-    property public abstract long displaySize;
+    property @Deprecated public abstract long displaySize;
     property public abstract androidx.compose.ui.test.MainTestClock mainClock;
   }
 
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt
index e860083..6ccf5cc 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt
+++ b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt
@@ -24,7 +24,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Recomposer
 import androidx.compose.ui.InternalComposeUiApi
-import androidx.compose.ui.node.Owner
+import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.platform.WindowRecomposerFactory
 import androidx.compose.ui.platform.WindowRecomposerPolicy
@@ -318,6 +318,10 @@
         Density(ApplicationProvider.getApplicationContext())
     }
 
+    @Deprecated(
+        "This utility was deprecated without replacement. It is recommend to use " +
+            "the root size for any assertions."
+    )
     override val displaySize by lazy {
         ApplicationProvider.getApplicationContext<Context>().resources.displayMetrics.let {
             IntSize(it.widthPixels, it.heightPixels)
@@ -526,7 +530,7 @@
 
         @SuppressLint("DocumentExceptions")
         override fun sendTextInputCommand(node: SemanticsNode, command: List<EditCommand>) {
-            val owner = node.owner as ViewRootForTest
+            val owner = node.root as ViewRootForTest
 
             @Suppress("DEPRECATION")
             runOnUiThread {
@@ -539,7 +543,7 @@
 
         @SuppressLint("DocumentExceptions")
         override fun sendImeAction(node: SemanticsNode, actionSpecified: ImeAction) {
-            val owner = node.owner as ViewRootForTest
+            val owner = node.root as ViewRootForTest
 
             @Suppress("DEPRECATION")
             runOnUiThread {
@@ -555,7 +559,7 @@
             return androidx.compose.ui.test.junit4.runOnUiThread(action)
         }
 
-        override fun getOwners(): Set<Owner> {
+        override fun getRoots(): Set<RootForTest> {
             // TODO(pavlis): Instead of returning a flatMap, let all consumers handle a tree
             //  structure. In case of multiple AndroidOwners, add a fake root
             waitForIdle()
diff --git a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
index 6d89edb5..c4fe79d 100644
--- a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
+++ b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
@@ -20,7 +20,7 @@
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.snapshots.Snapshot
-import androidx.compose.ui.node.Owner
+import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.platform.DesktopOwner
 import androidx.compose.ui.platform.DesktopOwners
 import androidx.compose.ui.platform.setContent
@@ -69,7 +69,12 @@
     override val mainClock: MainTestClock
         get() = TODO()
 
-    override val displaySize: IntSize get() = IntSize(1024, 768)
+    @Deprecated(
+        "This utility was deprecated without replacement. It is recommend to use " +
+            "the root size for any assertions."
+    )
+    override val displaySize: IntSize get() = testDisplaySize
+    internal val testDisplaySize: IntSize get() = IntSize(1024, 768)
 
     val executionQueue = LinkedList<() -> Unit>()
 
@@ -167,14 +172,14 @@
     }
 
     private fun performSetContent(composable: @Composable() () -> Unit) {
-        val surface = Surface.makeRasterN32Premul(displaySize.width, displaySize.height)!!
+        val surface = Surface.makeRasterN32Premul(testDisplaySize.width, testDisplaySize.height)!!
         val canvas = surface.canvas
         val owners = DesktopOwners(invalidate = {}).also {
             owners = it
         }
         val owner = DesktopOwner(owners)
         owner.setContent(content = composable)
-        owner.setSize(displaySize.width, displaySize.height)
+        owner.setSize(testDisplaySize.width, testDisplaySize.height)
         owner.measureAndLayout()
         owner.draw(canvas)
         this.owner = owner
@@ -207,7 +212,7 @@
             return rule.runOnUiThread(action)
         }
 
-        override fun getOwners(): Set<Owner> {
+        override fun getRoots(): Set<RootForTest> {
             return rule.owners!!.list
         }
 
diff --git a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.kt b/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.kt
index 51977f1..bd2f38f 100644
--- a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.kt
+++ b/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.kt
@@ -43,6 +43,10 @@
     /**
      * Current device display's size.
      */
+    @Deprecated(
+        "This utility was deprecated without replacement. It is recommend to use " +
+            "the root size for any assertions."
+    )
     val displaySize: IntSize get
 
     /**
diff --git a/compose/ui/ui-test/api/current.txt b/compose/ui/ui-test/api/current.txt
index 6641d7a..28ad28a 100644
--- a/compose/ui/ui-test/api/current.txt
+++ b/compose/ui/ui-test/api/current.txt
@@ -316,7 +316,7 @@
 
   @androidx.compose.ui.test.InternalTestApi public interface TestOwner {
     method public void advanceTimeBy(long millis);
-    method public java.util.Set<androidx.compose.ui.node.Owner> getOwners();
+    method public java.util.Set<androidx.compose.ui.node.RootForTest> getRoots();
     method public <T> T! runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
     method public void sendImeAction(androidx.compose.ui.semantics.SemanticsNode node, androidx.compose.ui.text.input.ImeAction actionSpecified);
     method public void sendTextInputCommand(androidx.compose.ui.semantics.SemanticsNode node, java.util.List<? extends androidx.compose.ui.text.input.EditCommand> command);
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 6641d7a..28ad28a 100644
--- a/compose/ui/ui-test/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-test/api/public_plus_experimental_current.txt
@@ -316,7 +316,7 @@
 
   @androidx.compose.ui.test.InternalTestApi public interface TestOwner {
     method public void advanceTimeBy(long millis);
-    method public java.util.Set<androidx.compose.ui.node.Owner> getOwners();
+    method public java.util.Set<androidx.compose.ui.node.RootForTest> getRoots();
     method public <T> T! runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
     method public void sendImeAction(androidx.compose.ui.semantics.SemanticsNode node, androidx.compose.ui.text.input.ImeAction actionSpecified);
     method public void sendTextInputCommand(androidx.compose.ui.semantics.SemanticsNode node, java.util.List<? extends androidx.compose.ui.text.input.EditCommand> command);
diff --git a/compose/ui/ui-test/api/restricted_current.txt b/compose/ui/ui-test/api/restricted_current.txt
index 6641d7a..28ad28a 100644
--- a/compose/ui/ui-test/api/restricted_current.txt
+++ b/compose/ui/ui-test/api/restricted_current.txt
@@ -316,7 +316,7 @@
 
   @androidx.compose.ui.test.InternalTestApi public interface TestOwner {
     method public void advanceTimeBy(long millis);
-    method public java.util.Set<androidx.compose.ui.node.Owner> getOwners();
+    method public java.util.Set<androidx.compose.ui.node.RootForTest> getRoots();
     method public <T> T! runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
     method public void sendImeAction(androidx.compose.ui.semantics.SemanticsNode node, androidx.compose.ui.text.input.ImeAction actionSpecified);
     method public void sendTextInputCommand(androidx.compose.ui.semantics.SemanticsNode node, java.util.List<? extends androidx.compose.ui.text.input.EditCommand> command);
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidAssertions.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidAssertions.kt
index 47c42c1..f96f634 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidAssertions.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidAssertions.kt
@@ -37,7 +37,7 @@
         return false
     }
 
-    (node.owner as? ViewRootForTest)?.let {
+    (node.root as? ViewRootForTest)?.let {
         if (!ViewMatchers.isDisplayed().matches(it.view)) {
             return false
         }
@@ -53,7 +53,7 @@
 }
 
 internal actual fun SemanticsNode.clippedNodeBoundsInWindow(): Rect {
-    val composeView = (owner as ViewRootForTest).view
+    val composeView = (root as ViewRootForTest).view
     val rootLocationInWindow = intArrayOf(0, 0).let {
         composeView.getLocationInWindow(it)
         Offset(it[0].toFloat(), it[1].toFloat())
@@ -62,7 +62,7 @@
 }
 
 internal actual fun SemanticsNode.isInScreenBounds(): Boolean {
-    val composeView = (owner as ViewRootForTest).view
+    val composeView = (root as ViewRootForTest).view
 
     // Window relative bounds of our node
     val nodeBoundsInWindow = clippedNodeBoundsInWindow()
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidImageHelpers.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidImageHelpers.kt
index e9de46c..7c82733 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidImageHelpers.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidImageHelpers.kt
@@ -53,7 +53,7 @@
         )
     }
 
-    val view = (node.owner as ViewRootForTest).view
+    val view = (node.root as ViewRootForTest).view
 
     // If we are in dialog use its window to capture the bitmap
     val dialogParentNodeMaybe = node.findClosestParentNode(includeSelf = true) {
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt
index 578cf62..0dde1da 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt
@@ -27,7 +27,7 @@
 import android.view.MotionEvent.ACTION_UP
 import androidx.compose.runtime.dispatch.AndroidUiDispatcher
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.node.Owner
+import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.platform.ViewRootForTest
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
@@ -35,20 +35,23 @@
 import kotlinx.coroutines.withContext
 import kotlin.math.max
 
-internal actual fun createInputDispatcher(testContext: TestContext, owner: Owner): InputDispatcher {
-    require(owner is ViewRootForTest) {
+internal actual fun createInputDispatcher(
+    testContext: TestContext,
+    root: RootForTest
+): InputDispatcher {
+    require(root is ViewRootForTest) {
         "InputDispatcher currently only supports dispatching to ViewRootForTest, not to " +
-            owner::class.java.simpleName
+            root::class.java.simpleName
     }
-    val view = owner.view
-    return AndroidInputDispatcher(testContext, owner) { view.dispatchTouchEvent(it) }
+    val view = root.view
+    return AndroidInputDispatcher(testContext, root) { view.dispatchTouchEvent(it) }
 }
 
 internal class AndroidInputDispatcher(
     private val testContext: TestContext,
-    composeRoot: ViewRootForTest?,
+    root: ViewRootForTest?,
     private val sendEvent: (MotionEvent) -> Unit
-) : InputDispatcher(testContext, composeRoot) {
+) : InputDispatcher(testContext, root) {
 
     private val batchLock = Any()
     // Batched events are generated just-in-time, given the "lateness" of the dispatching (see
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GestureScope.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GestureScope.kt
index 6b35b6d..621627c 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GestureScope.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GestureScope.kt
@@ -101,12 +101,9 @@
             "Can't query SemanticsNode, (Partial)GestureScope has already been disposed"
         }
 
-    // Convenience property
-    private val owner get() = semanticsNode.owner
-
     // TODO(b/133217292): Better error: explain which gesture couldn't be performed
     private var _inputDispatcher: InputDispatcher? =
-        createInputDispatcher(testContext, checkNotNull(owner))
+        createInputDispatcher(testContext, checkNotNull(semanticsNode.root))
     internal val inputDispatcher
         get() = checkNotNull(_inputDispatcher) {
             "Can't send gesture, (Partial)GestureScope has already been disposed"
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
index 8da027d..c3b6dde 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
@@ -17,11 +17,14 @@
 
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.lerp
-import androidx.compose.ui.node.Owner
+import androidx.compose.ui.node.RootForTest
 import kotlin.math.max
 import kotlin.math.roundToInt
 
-internal expect fun createInputDispatcher(testContext: TestContext, owner: Owner): InputDispatcher
+internal expect fun createInputDispatcher(
+    testContext: TestContext,
+    root: RootForTest
+): InputDispatcher
 
 /**
  * Dispatcher to inject full and partial gestures. An [InputDispatcher] is created at the
@@ -52,7 +55,7 @@
  */
 internal abstract class InputDispatcher(
     private val testContext: TestContext,
-    private val owner: Owner?
+    private val root: RootForTest?
 ) {
     companion object {
         /**
@@ -122,7 +125,7 @@
     protected abstract val now: Long
 
     init {
-        val state = testContext.states.remove(owner)
+        val state = testContext.states.remove(root)
         if (state?.partialGesture != null) {
             nextDownTime = state.nextDownTime
             gestureLateness = state.gestureLateness
@@ -130,9 +133,9 @@
         }
     }
 
-    protected open fun saveState(owner: Owner?) {
-        if (owner != null) {
-            testContext.states[owner] =
+    protected open fun saveState(root: RootForTest?) {
+        if (root != null) {
+            testContext.states[root] =
                 InputDispatcherState(
                     nextDownTime,
                     gestureLateness,
@@ -567,7 +570,7 @@
      * Called when this [InputDispatcher] is about to be discarded, from [GestureScope.dispose].
      */
     fun dispose() {
-        saveState(owner)
+        saveState(root)
         onDispose()
     }
 
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInputHelpers.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInputHelpers.kt
index 2e0e7e3..b28a86b 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInputHelpers.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInputHelpers.kt
@@ -26,8 +26,8 @@
  */
 fun SemanticsNodeInteraction.performKeyPress(keyEvent: KeyEvent): Boolean {
     val semanticsNode = fetchSemanticsNode("Failed to send key Event (${keyEvent.key})")
-    val owner = semanticsNode.owner
-    requireNotNull(owner) { "Failed to find owner" }
+    val root = semanticsNode.root
+    requireNotNull(root) { "Failed to find owner" }
     @OptIn(InternalTestApi::class)
-    return testContext.testOwner.runOnUiThread { owner.sendKeyEvent(keyEvent) }
+    return testContext.testOwner.runOnUiThread { root.sendKeyEvent(keyEvent) }
 }
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
index 74a2c6d..06abef4 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
@@ -248,7 +248,7 @@
     operation: Density.(SemanticsNode) -> R
 ): R {
     val node = fetchSemanticsNode("Failed to retrieve density for the node.")
-    val density = node.owner!!.density
+    val density = node.root!!.density
     return operation.invoke(density, node)
 }
 
@@ -256,7 +256,7 @@
     assertion: Density.(Rect) -> Unit
 ): SemanticsNodeInteraction {
     val node = fetchSemanticsNode("Failed to retrieve bounds of the node.")
-    val density = node.owner!!.density
+    val density = node.root!!.density
 
     assertion.invoke(density, node.unclippedBoundsInRoot)
     return this
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt
index 5f0afda..d44f775 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.ui.test
 
-import androidx.compose.ui.node.Owner
+import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.semantics.getAllSemanticsNodes
 import androidx.compose.ui.text.input.EditCommand
@@ -48,14 +48,14 @@
     fun <T> runOnUiThread(action: () -> T): T
 
     /**
-     * Collects all [Owner]s from all compose hierarchies.
+     * Collects all [RootForTest]s from all compose hierarchies.
      *
      * This is a blocking call. Returns only after compose is idle.
      *
      * Can crash in case it hits time out. This is not supposed to be handled as it
      * surfaces only in incorrect tests.
      */
-    fun getOwners(): Set<Owner>
+    fun getRoots(): Set<RootForTest>
 
     /**
      * Advances time if and only if this [TestOwner] uses a [MainTestClock]
@@ -74,7 +74,7 @@
  */
 @OptIn(InternalTestApi::class)
 internal fun TestOwner.getAllSemanticsNodes(useUnmergedTree: Boolean): List<SemanticsNode> {
-    return getOwners().flatMap { it.semanticsOwner.getAllSemanticsNodes(useUnmergedTree) }
+    return getRoots().flatMap { it.semanticsOwner.getAllSemanticsNodes(useUnmergedTree) }
 }
 
 @InternalTestApi
@@ -86,10 +86,10 @@
 class TestContext internal constructor(internal val testOwner: TestOwner) {
 
     /**
-     * Stores the [InputDispatcherState] of each [Owner]. The state will be restored in an
+     * Stores the [InputDispatcherState] of each [RootForTest]. The state will be restored in an
      * [InputDispatcher] when it is created for an owner that has a state stored.
      */
-    internal val states = mutableMapOf<Owner, InputDispatcherState>()
+    internal val states = mutableMapOf<RootForTest, InputDispatcherState>()
 
     internal fun getAllSemanticsNodes(mergingEnabled: Boolean) =
         testOwner.getAllSemanticsNodes(mergingEnabled)
diff --git a/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/DesktopInputDispatcher.kt b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/DesktopInputDispatcher.kt
index 2d1f927..4aaddd3 100644
--- a/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/DesktopInputDispatcher.kt
+++ b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/DesktopInputDispatcher.kt
@@ -18,17 +18,20 @@
 
 import androidx.compose.ui.input.pointer.PointerId
 import androidx.compose.ui.input.pointer.TestPointerInputEventData
-import androidx.compose.ui.node.Owner
+import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.platform.DesktopOwner
 
-internal actual fun createInputDispatcher(testContext: TestContext, owner: Owner): InputDispatcher {
-    return DesktopInputDispatcher(testContext, owner as DesktopOwner)
+internal actual fun createInputDispatcher(
+    testContext: TestContext,
+    root: RootForTest
+): InputDispatcher {
+    return DesktopInputDispatcher(testContext, root as DesktopOwner)
 }
 
 internal class DesktopInputDispatcher(
     testContext: TestContext,
-    val owner: DesktopOwner
-) : InputDispatcher(testContext, owner) {
+    val root: DesktopOwner
+) : InputDispatcher(testContext, root) {
     companion object {
         var gesturePointerId = 0L
     }
@@ -86,7 +89,7 @@
                     Thread.sleep(delayMs)
                 }
             }
-            owner.processPointerInput(eventTime, it)
+            root.processPointerInput(eventTime, it)
         }
     }
 
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/TestAnimationPreview.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/TestAnimationPreview.kt
index 232a23f..17e63cf 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/TestAnimationPreview.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/TestAnimationPreview.kt
@@ -71,6 +71,6 @@
         shape = MaterialTheme.shapes.large.copy(topLeft = CornerSize(state[CheckBoxCorner])),
         modifier = Modifier.toggleable(value = selected, >
     ) {
-        Icon(imageVector = Icons.Filled.Done)
+        Icon(imageVector = Icons.Filled.Done, contentDescription = null)
     }
 }
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTreeTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTreeTest.kt
index 2c25f26..eeb8a85 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTreeTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/LayoutInspectorTreeTest.kt
@@ -93,7 +93,7 @@
             Inspectable(slotTableRecord) {
                 Column {
                     Text(text = "Hello World", color = Color.Green)
-                    Icon(Icons.Filled.FavoriteBorder)
+                    Icon(Icons.Filled.FavoriteBorder, null)
                     Surface {
                         Button( { Text(text = "OK") }
                     }
@@ -309,7 +309,7 @@
                 Column {
                     Text(text = "Hello World", color = Color.Green)
                     Spacer(Modifier.preferredHeight(16.dp))
-                    Image(Icons.Filled.Call)
+                    Image(Icons.Filled.Call, null)
                 }
             }
         }
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 90634da..863dbec 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -2086,64 +2086,6 @@
     property public abstract long layerId;
   }
 
-  public interface Owner {
-    method public long calculatePosition-nOcc-ac();
-    method public long calculatePositionInWindow-nOcc-ac();
-    method public androidx.compose.ui.node.OwnedLayer createLayer(kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Canvas,kotlin.Unit> drawBlock, kotlin.jvm.functions.Function0<kotlin.Unit> invalidateParentLayer);
-    method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.autofill.Autofill? getAutofill();
-    method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.autofill.AutofillTree getAutofillTree();
-    method public androidx.compose.ui.platform.ClipboardManager getClipboardManager();
-    method public androidx.compose.ui.unit.Density getDensity();
-    method public androidx.compose.ui.focus.FocusManager getFocusManager();
-    method public androidx.compose.ui.text.font.Font.ResourceLoader getFontLoader();
-    method public androidx.compose.ui.hapticfeedback.HapticFeedback getHapticFeedBack();
-    method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
-    method public long getMeasureIteration();
-    method public androidx.compose.ui.node.LayoutNode getRoot();
-    method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
-    method public boolean getShowLayoutBounds();
-    method public androidx.compose.ui.node.OwnerSnapshotObserver getSnapshotObserver();
-    method public androidx.compose.ui.text.input.TextInputService getTextInputService();
-    method public androidx.compose.ui.platform.TextToolbar getTextToolbar();
-    method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
-    method public androidx.compose.ui.platform.WindowInfo getWindowInfo();
-    method @Deprecated public default androidx.compose.ui.platform.WindowInfo! getWindowManager();
-    method public void measureAndLayout();
-    method public void onAttach(androidx.compose.ui.node.LayoutNode node);
-    method public void onDetach(androidx.compose.ui.node.LayoutNode node);
-    method public void onLayoutChange(androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void onRequestMeasure(androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void onRequestRelayout(androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void onSemanticsChange();
-    method public boolean requestFocus();
-    method public boolean sendKeyEvent-ZmokQxo(android.view.KeyEvent keyEvent);
-    property @androidx.compose.ui.ExperimentalComposeUiApi public abstract androidx.compose.ui.autofill.Autofill? autofill;
-    property @androidx.compose.ui.ExperimentalComposeUiApi public abstract androidx.compose.ui.autofill.AutofillTree autofillTree;
-    property public abstract androidx.compose.ui.platform.ClipboardManager clipboardManager;
-    property public abstract androidx.compose.ui.unit.Density density;
-    property public abstract androidx.compose.ui.focus.FocusManager focusManager;
-    property public abstract androidx.compose.ui.text.font.Font.ResourceLoader fontLoader;
-    property public abstract androidx.compose.ui.hapticfeedback.HapticFeedback hapticFeedBack;
-    property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
-    property public abstract long measureIteration;
-    property public abstract androidx.compose.ui.node.LayoutNode root;
-    property public abstract androidx.compose.ui.semantics.SemanticsOwner semanticsOwner;
-    property public abstract boolean showLayoutBounds;
-    property public abstract androidx.compose.ui.node.OwnerSnapshotObserver snapshotObserver;
-    property public abstract androidx.compose.ui.text.input.TextInputService textInputService;
-    property public abstract androidx.compose.ui.platform.TextToolbar textToolbar;
-    property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
-    property public abstract androidx.compose.ui.platform.WindowInfo windowInfo;
-    property @Deprecated public default androidx.compose.ui.platform.WindowInfo! windowManager;
-    field public static final androidx.compose.ui.node.Owner.Companion Companion;
-  }
-
-  public static final class Owner.Companion {
-    method public boolean getEnableExtraAssertions();
-    method public void setEnableExtraAssertions(boolean p);
-    property public final boolean enableExtraAssertions;
-  }
-
   public interface OwnerScope {
     method public boolean isValid();
     property public abstract boolean isValid;
@@ -2160,6 +2102,16 @@
     property public final T? value;
   }
 
+  public interface RootForTest {
+    method public androidx.compose.ui.unit.Density getDensity();
+    method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
+    method public androidx.compose.ui.text.input.TextInputService getTextInputService();
+    method public boolean sendKeyEvent-ZmokQxo(android.view.KeyEvent keyEvent);
+    property public abstract androidx.compose.ui.unit.Density density;
+    property public abstract androidx.compose.ui.semantics.SemanticsOwner semanticsOwner;
+    property public abstract androidx.compose.ui.text.input.TextInputService textInputService;
+  }
+
   public final class UiApplier extends androidx.compose.runtime.AbstractApplier<java.lang.Object> {
     ctor public UiApplier(Object root);
     method public void insertBottomUp(int index, Object instance);
@@ -2401,7 +2353,7 @@
     property public abstract float touchSlop;
   }
 
-  @VisibleForTesting public interface ViewRootForTest extends androidx.compose.ui.node.Owner {
+  @VisibleForTesting public interface ViewRootForTest extends androidx.compose.ui.node.RootForTest {
     method public boolean getHasPendingMeasureOrLayout();
     method public android.view.View getView();
     method public void invalidateDescendants();
@@ -2754,9 +2706,9 @@
     method public int getId();
     method public androidx.compose.ui.layout.LayoutInfo getLayoutInfo();
     method public boolean getMergingEnabled();
-    method public androidx.compose.ui.node.Owner? getOwner();
     method public androidx.compose.ui.semantics.SemanticsNode? getParent();
     method public long getPositionInRoot-F1C5BW0();
+    method public androidx.compose.ui.node.RootForTest? getRoot();
     method public long getSize-YbymL2g();
     method public boolean isRoot();
     property public final androidx.compose.ui.geometry.Rect boundsInRoot;
@@ -2768,9 +2720,9 @@
     property public final boolean isRoot;
     property public final androidx.compose.ui.layout.LayoutInfo layoutInfo;
     property public final boolean mergingEnabled;
-    property public final androidx.compose.ui.node.Owner? owner;
     property public final androidx.compose.ui.semantics.SemanticsNode? parent;
     property public final long positionInRoot;
+    property public final androidx.compose.ui.node.RootForTest? root;
     property public final long size;
   }
 
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 90634da..863dbec 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -2086,64 +2086,6 @@
     property public abstract long layerId;
   }
 
-  public interface Owner {
-    method public long calculatePosition-nOcc-ac();
-    method public long calculatePositionInWindow-nOcc-ac();
-    method public androidx.compose.ui.node.OwnedLayer createLayer(kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Canvas,kotlin.Unit> drawBlock, kotlin.jvm.functions.Function0<kotlin.Unit> invalidateParentLayer);
-    method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.autofill.Autofill? getAutofill();
-    method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.autofill.AutofillTree getAutofillTree();
-    method public androidx.compose.ui.platform.ClipboardManager getClipboardManager();
-    method public androidx.compose.ui.unit.Density getDensity();
-    method public androidx.compose.ui.focus.FocusManager getFocusManager();
-    method public androidx.compose.ui.text.font.Font.ResourceLoader getFontLoader();
-    method public androidx.compose.ui.hapticfeedback.HapticFeedback getHapticFeedBack();
-    method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
-    method public long getMeasureIteration();
-    method public androidx.compose.ui.node.LayoutNode getRoot();
-    method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
-    method public boolean getShowLayoutBounds();
-    method public androidx.compose.ui.node.OwnerSnapshotObserver getSnapshotObserver();
-    method public androidx.compose.ui.text.input.TextInputService getTextInputService();
-    method public androidx.compose.ui.platform.TextToolbar getTextToolbar();
-    method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
-    method public androidx.compose.ui.platform.WindowInfo getWindowInfo();
-    method @Deprecated public default androidx.compose.ui.platform.WindowInfo! getWindowManager();
-    method public void measureAndLayout();
-    method public void onAttach(androidx.compose.ui.node.LayoutNode node);
-    method public void onDetach(androidx.compose.ui.node.LayoutNode node);
-    method public void onLayoutChange(androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void onRequestMeasure(androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void onRequestRelayout(androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void onSemanticsChange();
-    method public boolean requestFocus();
-    method public boolean sendKeyEvent-ZmokQxo(android.view.KeyEvent keyEvent);
-    property @androidx.compose.ui.ExperimentalComposeUiApi public abstract androidx.compose.ui.autofill.Autofill? autofill;
-    property @androidx.compose.ui.ExperimentalComposeUiApi public abstract androidx.compose.ui.autofill.AutofillTree autofillTree;
-    property public abstract androidx.compose.ui.platform.ClipboardManager clipboardManager;
-    property public abstract androidx.compose.ui.unit.Density density;
-    property public abstract androidx.compose.ui.focus.FocusManager focusManager;
-    property public abstract androidx.compose.ui.text.font.Font.ResourceLoader fontLoader;
-    property public abstract androidx.compose.ui.hapticfeedback.HapticFeedback hapticFeedBack;
-    property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
-    property public abstract long measureIteration;
-    property public abstract androidx.compose.ui.node.LayoutNode root;
-    property public abstract androidx.compose.ui.semantics.SemanticsOwner semanticsOwner;
-    property public abstract boolean showLayoutBounds;
-    property public abstract androidx.compose.ui.node.OwnerSnapshotObserver snapshotObserver;
-    property public abstract androidx.compose.ui.text.input.TextInputService textInputService;
-    property public abstract androidx.compose.ui.platform.TextToolbar textToolbar;
-    property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
-    property public abstract androidx.compose.ui.platform.WindowInfo windowInfo;
-    property @Deprecated public default androidx.compose.ui.platform.WindowInfo! windowManager;
-    field public static final androidx.compose.ui.node.Owner.Companion Companion;
-  }
-
-  public static final class Owner.Companion {
-    method public boolean getEnableExtraAssertions();
-    method public void setEnableExtraAssertions(boolean p);
-    property public final boolean enableExtraAssertions;
-  }
-
   public interface OwnerScope {
     method public boolean isValid();
     property public abstract boolean isValid;
@@ -2160,6 +2102,16 @@
     property public final T? value;
   }
 
+  public interface RootForTest {
+    method public androidx.compose.ui.unit.Density getDensity();
+    method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
+    method public androidx.compose.ui.text.input.TextInputService getTextInputService();
+    method public boolean sendKeyEvent-ZmokQxo(android.view.KeyEvent keyEvent);
+    property public abstract androidx.compose.ui.unit.Density density;
+    property public abstract androidx.compose.ui.semantics.SemanticsOwner semanticsOwner;
+    property public abstract androidx.compose.ui.text.input.TextInputService textInputService;
+  }
+
   public final class UiApplier extends androidx.compose.runtime.AbstractApplier<java.lang.Object> {
     ctor public UiApplier(Object root);
     method public void insertBottomUp(int index, Object instance);
@@ -2401,7 +2353,7 @@
     property public abstract float touchSlop;
   }
 
-  @VisibleForTesting public interface ViewRootForTest extends androidx.compose.ui.node.Owner {
+  @VisibleForTesting public interface ViewRootForTest extends androidx.compose.ui.node.RootForTest {
     method public boolean getHasPendingMeasureOrLayout();
     method public android.view.View getView();
     method public void invalidateDescendants();
@@ -2754,9 +2706,9 @@
     method public int getId();
     method public androidx.compose.ui.layout.LayoutInfo getLayoutInfo();
     method public boolean getMergingEnabled();
-    method public androidx.compose.ui.node.Owner? getOwner();
     method public androidx.compose.ui.semantics.SemanticsNode? getParent();
     method public long getPositionInRoot-F1C5BW0();
+    method public androidx.compose.ui.node.RootForTest? getRoot();
     method public long getSize-YbymL2g();
     method public boolean isRoot();
     property public final androidx.compose.ui.geometry.Rect boundsInRoot;
@@ -2768,9 +2720,9 @@
     property public final boolean isRoot;
     property public final androidx.compose.ui.layout.LayoutInfo layoutInfo;
     property public final boolean mergingEnabled;
-    property public final androidx.compose.ui.node.Owner? owner;
     property public final androidx.compose.ui.semantics.SemanticsNode? parent;
     property public final long positionInRoot;
+    property public final androidx.compose.ui.node.RootForTest? root;
     property public final long size;
   }
 
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index e6be08b..9ba676f 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -2148,64 +2148,6 @@
     property public abstract long layerId;
   }
 
-  public interface Owner {
-    method public long calculatePosition-nOcc-ac();
-    method public long calculatePositionInWindow-nOcc-ac();
-    method public androidx.compose.ui.node.OwnedLayer createLayer(kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Canvas,kotlin.Unit> drawBlock, kotlin.jvm.functions.Function0<kotlin.Unit> invalidateParentLayer);
-    method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.autofill.Autofill? getAutofill();
-    method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.autofill.AutofillTree getAutofillTree();
-    method public androidx.compose.ui.platform.ClipboardManager getClipboardManager();
-    method public androidx.compose.ui.unit.Density getDensity();
-    method public androidx.compose.ui.focus.FocusManager getFocusManager();
-    method public androidx.compose.ui.text.font.Font.ResourceLoader getFontLoader();
-    method public androidx.compose.ui.hapticfeedback.HapticFeedback getHapticFeedBack();
-    method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
-    method public long getMeasureIteration();
-    method public androidx.compose.ui.node.LayoutNode getRoot();
-    method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
-    method public boolean getShowLayoutBounds();
-    method public androidx.compose.ui.node.OwnerSnapshotObserver getSnapshotObserver();
-    method public androidx.compose.ui.text.input.TextInputService getTextInputService();
-    method public androidx.compose.ui.platform.TextToolbar getTextToolbar();
-    method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
-    method public androidx.compose.ui.platform.WindowInfo getWindowInfo();
-    method @Deprecated public default androidx.compose.ui.platform.WindowInfo! getWindowManager();
-    method public void measureAndLayout();
-    method public void onAttach(androidx.compose.ui.node.LayoutNode node);
-    method public void onDetach(androidx.compose.ui.node.LayoutNode node);
-    method public void onLayoutChange(androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void onRequestMeasure(androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void onRequestRelayout(androidx.compose.ui.node.LayoutNode layoutNode);
-    method public void onSemanticsChange();
-    method public boolean requestFocus();
-    method public boolean sendKeyEvent-ZmokQxo(android.view.KeyEvent keyEvent);
-    property @androidx.compose.ui.ExperimentalComposeUiApi public abstract androidx.compose.ui.autofill.Autofill? autofill;
-    property @androidx.compose.ui.ExperimentalComposeUiApi public abstract androidx.compose.ui.autofill.AutofillTree autofillTree;
-    property public abstract androidx.compose.ui.platform.ClipboardManager clipboardManager;
-    property public abstract androidx.compose.ui.unit.Density density;
-    property public abstract androidx.compose.ui.focus.FocusManager focusManager;
-    property public abstract androidx.compose.ui.text.font.Font.ResourceLoader fontLoader;
-    property public abstract androidx.compose.ui.hapticfeedback.HapticFeedback hapticFeedBack;
-    property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
-    property public abstract long measureIteration;
-    property public abstract androidx.compose.ui.node.LayoutNode root;
-    property public abstract androidx.compose.ui.semantics.SemanticsOwner semanticsOwner;
-    property public abstract boolean showLayoutBounds;
-    property public abstract androidx.compose.ui.node.OwnerSnapshotObserver snapshotObserver;
-    property public abstract androidx.compose.ui.text.input.TextInputService textInputService;
-    property public abstract androidx.compose.ui.platform.TextToolbar textToolbar;
-    property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
-    property public abstract androidx.compose.ui.platform.WindowInfo windowInfo;
-    property @Deprecated public default androidx.compose.ui.platform.WindowInfo! windowManager;
-    field public static final androidx.compose.ui.node.Owner.Companion Companion;
-  }
-
-  public static final class Owner.Companion {
-    method public boolean getEnableExtraAssertions();
-    method public void setEnableExtraAssertions(boolean p);
-    property public final boolean enableExtraAssertions;
-  }
-
   public interface OwnerScope {
     method public boolean isValid();
     property public abstract boolean isValid;
@@ -2222,6 +2164,16 @@
     property public final T? value;
   }
 
+  public interface RootForTest {
+    method public androidx.compose.ui.unit.Density getDensity();
+    method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
+    method public androidx.compose.ui.text.input.TextInputService getTextInputService();
+    method public boolean sendKeyEvent-ZmokQxo(android.view.KeyEvent keyEvent);
+    property public abstract androidx.compose.ui.unit.Density density;
+    property public abstract androidx.compose.ui.semantics.SemanticsOwner semanticsOwner;
+    property public abstract androidx.compose.ui.text.input.TextInputService textInputService;
+  }
+
   public final class UiApplier extends androidx.compose.runtime.AbstractApplier<java.lang.Object> {
     ctor public UiApplier(Object root);
     method public void insertBottomUp(int index, Object instance);
@@ -2463,7 +2415,7 @@
     property public abstract float touchSlop;
   }
 
-  @VisibleForTesting public interface ViewRootForTest extends androidx.compose.ui.node.Owner {
+  @VisibleForTesting public interface ViewRootForTest extends androidx.compose.ui.node.RootForTest {
     method public boolean getHasPendingMeasureOrLayout();
     method public android.view.View getView();
     method public void invalidateDescendants();
@@ -2816,9 +2768,9 @@
     method public int getId();
     method public androidx.compose.ui.layout.LayoutInfo getLayoutInfo();
     method public boolean getMergingEnabled();
-    method public androidx.compose.ui.node.Owner? getOwner();
     method public androidx.compose.ui.semantics.SemanticsNode? getParent();
     method public long getPositionInRoot-F1C5BW0();
+    method public androidx.compose.ui.node.RootForTest? getRoot();
     method public long getSize-YbymL2g();
     method public boolean isRoot();
     property public final androidx.compose.ui.geometry.Rect boundsInRoot;
@@ -2830,9 +2782,9 @@
     property public final boolean isRoot;
     property public final androidx.compose.ui.layout.LayoutInfo layoutInfo;
     property public final boolean mergingEnabled;
-    property public final androidx.compose.ui.node.Owner? owner;
     property public final androidx.compose.ui.semantics.SemanticsNode? parent;
     property public final long positionInRoot;
+    property public final androidx.compose.ui.node.RootForTest? root;
     property public final long size;
   }
 
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index 952edf6..108a403 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -21,6 +21,7 @@
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 import static androidx.build.dependencies.DependenciesKt.*
+import static androidx.inspection.gradle.InspectionPluginKt.packageInspector
 
 plugins {
     id("AndroidXPlugin")
@@ -109,6 +110,8 @@
     }
 }
 
+packageInspector(project, project(":compose:ui:ui-inspection"))
+
 if(AndroidXUiPlugin.isMultiplatformEnabled(project)) {
     kotlin {
         android()
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/VectorGraphicsDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/VectorGraphicsDemo.kt
index 8465693..5686f60 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/VectorGraphicsDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/VectorGraphicsDemo.kt
@@ -51,6 +51,7 @@
         imageVector.resource.resource?.let {
             Image(
                 imageVector = it,
+                contentDescription = "Crane",
                 modifier = Modifier.preferredSize(200.dp, 200.dp),
                 contentScale = ContentScale.Inside
             )
@@ -60,6 +61,7 @@
         complexImageVector.resource.resource?.let {
             Image(
                 imageVector = it,
+                contentDescription = "Hourglass",
                 modifier = Modifier.preferredSize(64.dp, 64.dp),
                 contentScale = ContentScale.Fit
             )
@@ -67,6 +69,7 @@
 
         Image(
             painter = vectorShape(120.dp, 120.dp),
+            contentDescription = null,
             modifier = Modifier.preferredSize(200.dp, 150.dp)
         )
     }
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt
index 15c1dbd..cb5f8ad 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/DrawModifierSample.kt
@@ -111,6 +111,7 @@
     }
     Image(
         painter = vectorPainter,
+        contentDescription = null,
         modifier = Modifier.size(120.dp).drawWithCache {
             val gradient = Brush.linearGradient(
                 colors = listOf(Color.Red, Color.Blue),
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/PainterSample.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/PainterSample.kt
index dfce282..48f7a8e 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/PainterSample.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/PainterSample.kt
@@ -66,6 +66,7 @@
     // in the landscape orientation based on the res/drawable and res/drawable-land-hdpi folders
     Image(
         painterResource(R.drawable.ic_vector_or_png),
+        contentDescription = null,
         modifier = Modifier.size(50.dp)
     )
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
index 66d65f3..3847526 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
@@ -233,13 +233,14 @@
             val clickState = remember { mutableStateOf(false) }
             Image(
                 imageVector = if (clickState.value) icon1 else icon2,
+                contentDescription = null,
                 modifier = Modifier
                     .testTag(testTag)
                     .preferredSize(icon1.defaultWidth, icon1.defaultHeight)
                     .background(Color.Red)
                     .clickable { clickState.value = !clickState.value },
-                contentScale = ContentScale.FillHeight,
-                alignment = Alignment.TopStart
+                alignment = Alignment.TopStart,
+                contentScale = ContentScale.FillHeight
             )
         }
 
@@ -292,6 +293,7 @@
             }
             Image(
                 painter = vectorPainter,
+                contentDescription = null,
                 modifier = Modifier
                     .testTag(testTag)
                     .preferredSize(defaultWidth * 7, defaultHeight * 3)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
index 9538232..c28b85c 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
@@ -24,7 +24,6 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.hapticfeedback.HapticFeedback
-import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
@@ -34,11 +33,11 @@
 import androidx.compose.ui.node.OwnedLayer
 import androidx.compose.ui.node.Owner
 import androidx.compose.ui.node.OwnerSnapshotObserver
+import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.platform.ClipboardManager
 import androidx.compose.ui.platform.TextToolbar
 import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.platform.WindowInfo
-import androidx.compose.ui.semantics.SemanticsOwner
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.input.TextInputService
 import androidx.compose.ui.unit.Constraints
@@ -3037,9 +3036,10 @@
     override fun calculatePositionInWindow(): IntOffset = position
 
     override fun requestFocus(): Boolean = false
-    override fun sendKeyEvent(keyEvent: KeyEvent): Boolean = false
     override val root: LayoutNode
         get() = targetRoot
+    override val rootForTest: RootForTest
+        get() = TODO("Not yet implemented")
     override val hapticFeedBack: HapticFeedback
         get() = TODO("Not yet implemented")
     override val clipboardManager: ClipboardManager
@@ -3052,8 +3052,6 @@
         get() = null
     override val density: Density
         get() = Density(1f)
-    override val semanticsOwner: SemanticsOwner
-        get() = TODO("Not yet implemented")
     override val textInputService: TextInputService
         get() = TODO("Not yet implemented")
     override val focusManager: FocusManager
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
index 73e2715..186a2b0 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
@@ -92,6 +92,43 @@
     }
 
     @Test
+    @SmallTest
+    fun internalSizeChange() {
+        var latch = CountDownLatch(1)
+        var changedSize = IntSize.Zero
+        var sizePx by mutableStateOf(10)
+
+        rule.runOnUiThread {
+            activity.setContent {
+                with(AmbientDensity.current) {
+                    Box(
+                        Modifier.padding(10.toDp())
+                            .onSizeChanged {
+                                changedSize = it
+                                latch.countDown()
+                            }.padding(sizePx.toDp())
+                    ) {
+                        Box(Modifier.size(10.toDp()))
+                    }
+                }
+            }
+        }
+
+        // Initial setting will call onSizeChanged
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+        assertEquals(30, changedSize.height)
+        assertEquals(30, changedSize.width)
+
+        latch = CountDownLatch(1)
+        sizePx = 20
+
+        // We've changed the size of the contents, so we should receive a onSizeChanged call
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
+        assertEquals(50, changedSize.height)
+        assertEquals(50, changedSize.width)
+    }
+
+    @Test
     fun onlyInnerSizeChange() {
         var latch = CountDownLatch(1)
         var changedSize = IntSize.Zero
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
index dcdc42e..0a89378 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
@@ -124,7 +124,7 @@
     private fun isSecureFlagEnabledForDialog(): Boolean {
         val owner = rule
             .onNode(isDialog())
-            .fetchSemanticsNode("").owner as View
+            .fetchSemanticsNode("").root as View
         return (owner.rootView.layoutParams as WindowManager.LayoutParams).flags and
             WindowManager.LayoutParams.FLAG_SECURE != 0
     }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogUiTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogUiTest.kt
index 8b464cd..285f48d 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogUiTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogUiTest.kt
@@ -23,9 +23,13 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
+import androidx.compose.ui.test.isRoot
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onFirst
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.height
 import androidx.test.espresso.Espresso
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
@@ -117,7 +121,9 @@
 
         // Click outside the dialog to dismiss it
         val outsideX = 0
-        val outsideY = rule.displaySize.height / 2
+        val outsideY = with(rule.density) {
+            rule.onAllNodes(isRoot()).onFirst().getUnclippedBoundsInRoot().height.toIntPx() / 2
+        }
         UiDevice.getInstance(getInstrumentation()).click(outsideX, outsideY)
 
         rule.onNodeWithText(defaultText).assertDoesNotExist()
@@ -139,7 +145,9 @@
 
         // Click outside the dialog to try to dismiss it
         val outsideX = 0
-        val outsideY = rule.displaySize.height / 2
+        val outsideY = with(rule.density) {
+            rule.onAllNodes(isRoot()).onFirst().getUnclippedBoundsInRoot().height.toIntPx() / 2
+        }
         UiDevice.getInstance(getInstrumentation()).click(outsideX, outsideY)
 
         // The Dialog should still be visible
@@ -167,7 +175,9 @@
 
         // Click outside the dialog to try to dismiss it
         val outsideX = 0
-        val outsideY = rule.displaySize.height / 2
+        val outsideY = with(rule.density) {
+            rule.onAllNodes(isRoot()).onFirst().getUnclippedBoundsInRoot().height.toIntPx() / 2
+        }
         UiDevice.getInstance(getInstrumentation()).click(outsideX, outsideY)
 
         // The Dialog should still be visible
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 da1cd3d..2ace3e0 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
@@ -64,6 +64,7 @@
 import androidx.compose.ui.node.OwnedLayer
 import androidx.compose.ui.node.Owner
 import androidx.compose.ui.node.OwnerSnapshotObserver
+import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.semantics.SemanticsModifierCore
 import androidx.compose.ui.semantics.SemanticsOwner
 import androidx.compose.ui.text.InternalTextApi
@@ -130,6 +131,8 @@
             .then(keyInputModifier)
     }
 
+    override val rootForTest: RootForTest = this
+
     override val semanticsOwner: SemanticsOwner = SemanticsOwner(root)
     private val accessibilityDelegate = AndroidComposeViewAccessibilityDelegateCompat(this)
 
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewRootForTest.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewRootForTest.kt
index 461e12a..77445b1 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewRootForTest.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewRootForTest.kt
@@ -18,15 +18,14 @@
 
 import android.view.View
 import androidx.annotation.VisibleForTesting
-import androidx.compose.ui.node.Owner
+import androidx.compose.ui.node.RootForTest
 
 /**
  * The marker interface to be implemented by the [View] backing the composition.
  * To be used in tests.
  */
 @VisibleForTesting
-// TODO(b/174747742) Introduce RootForTest and extend it instead of Owner
-interface ViewRootForTest : Owner {
+interface ViewRootForTest : RootForTest {
 
     /**
      * The view backing this Owner.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnRemeasuredModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnRemeasuredModifier.kt
index 338fcaf..90d7e8b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnRemeasuredModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/OnRemeasuredModifier.kt
@@ -23,14 +23,17 @@
 import androidx.compose.ui.unit.IntSize
 
 /**
- * Invoke [onSizeChanged] with the size of the content after it has been measured.
- * This will only be invoked either the first time measurement happens or when the content
+ * Invoke [onSizeChanged] when the size of the modifier immediately after it has changed. If
+ * there is no modifier following [onSizeChanged], the content size of the layout is reported.
+ *
+ * [onSizeChanged] will only be invoked during the first time measurement or when the
  * size has changed.
  *
- * Use [Layout] or [SubcomposeLayout] to have the size of one component to affect the size
+ * Use [Layout] or [SubcomposeLayout] to have the size of one component affect the size
  * of another component. Using the size received from the [onSizeChanged] callback in a
  * [MutableState] to affect layout will cause the new value to be recomposed and read only in the
- * following frame, causing a one frame lag.
+ * following frame, causing a one frame lag. You can use [onSizeChanged] to affect
+ * drawing operations.
  *
  * Example usage:
  * @sample androidx.compose.ui.samples.OnSizeChangedSample
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 7f1d771..c4b79fe 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
@@ -60,7 +60,6 @@
 import androidx.compose.ui.semantics.outerSemantics
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.util.deleteAt
 import kotlin.math.roundToInt
@@ -642,7 +641,6 @@
             }
             val addedCallback = hasNewPositioningCallback()
             onPositionedCallbacks.clear()
-            onRemeasuredCallbacks.clear()
 
             // Create a new chain of LayoutNodeWrappers, reusing existing ones from wrappers
             // when possible.
@@ -651,9 +649,6 @@
                 if (mod is OnGloballyPositionedModifier) {
                     onPositionedCallbacks += mod
                 }
-                if (mod is OnRemeasuredModifier) {
-                    onRemeasuredCallbacks += mod
-                }
                 if (mod is RemeasurementModifier) {
                     mod.onRemeasurementAvailable(this)
                 }
@@ -699,6 +694,9 @@
                     if (mod is SemanticsModifier) {
                         wrapper = SemanticsWrapper(wrapper, mod).assignChained(toWrap)
                     }
+                    if (mod is OnRemeasuredModifier) {
+                        wrapper = RemeasureModifierWrapper(wrapper, mod).assignChained(toWrap)
+                    }
                 }
                 wrapper
             }
@@ -770,11 +768,6 @@
     private val >
 
     /**
-     * List of all OnSizeChangedModifiers in the modifier chain.
-     */
-    private val >
-
-    /**
      * Flag used by [OnPositionedDispatcher] to identify LayoutNodes that have already
      * had their [OnGloballyPositionedModifier]'s dispatch called so that they aren't called
      * multiple times.
@@ -1070,16 +1063,6 @@
         innerLayoutNodeWrapper.measureResult = measureResult
         this.providedAlignmentLines.clear()
         this.providedAlignmentLines += measureResult.alignmentLines
-
-        if (onRemeasuredCallbacks.isNotEmpty()) {
-            val invokeRemeasureCallbacks = {
-                val content = innerLayoutNodeWrapper
-                val size = IntSize(content.measuredWidth, content.measuredHeight)
-                onRemeasuredCallbacks.forEach { it.onRemeasured(size) }
-            }
-            owner?.snapshotObserver?.pauseSnapshotReadObservation(invokeRemeasureCallbacks)
-                ?: invokeRemeasureCallbacks.invoke()
-        }
     }
 
     /**
@@ -1389,4 +1372,4 @@
         wrapper.isChained = true
     }
     return this
-}
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
index 9ec0253..64f47eb 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
@@ -21,12 +21,10 @@
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.hapticfeedback.HapticFeedback
-import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.platform.ClipboardManager
 import androidx.compose.ui.platform.TextToolbar
 import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.platform.WindowInfo
-import androidx.compose.ui.semantics.SemanticsOwner
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.input.TextInputService
 import androidx.compose.ui.unit.Density
@@ -38,13 +36,15 @@
  * to Android [views][android.view.View] and all layout, draw, input, and accessibility is hooked
  * through them.
  */
-interface Owner {
+internal interface Owner {
 
     /**
      * The root layout node in the component tree.
      */
     val root: LayoutNode
 
+    val rootForTest: RootForTest
+
     /**
      * Provide haptic feedback to the user. Use the Android version of haptic feedback.
      */
@@ -79,8 +79,6 @@
 
     val density: Density
 
-    val semanticsOwner: SemanticsOwner
-
     val textInputService: TextInputService
 
     /**
@@ -158,13 +156,6 @@
     fun requestFocus(): Boolean
 
     /**
-     * Send this [KeyEvent] to the focused component in this [Owner].
-     *
-     * @return true if the event was consumed. False otherwise.
-     */
-    fun sendKeyEvent(keyEvent: KeyEvent): Boolean
-
-    /**
      * Iterates through all LayoutNodes that have requested layout and measures and lays them out
      */
     fun measureAndLayout()
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RemeasureModifierWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RemeasureModifierWrapper.kt
new file mode 100644
index 0000000..6d25308
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RemeasureModifierWrapper.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.node
+
+import androidx.compose.ui.layout.OnRemeasuredModifier
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.unit.Constraints
+
+/**
+ * Wrapper around the [OnRemeasuredModifier] to notify whenever a remeasurement happens.
+ */
+internal class RemeasureModifierWrapper(
+    wrapped: LayoutNodeWrapper,
+    modifier: OnRemeasuredModifier
+) : DelegatingLayoutNodeWrapper<OnRemeasuredModifier>(wrapped, modifier) {
+    override fun performMeasure(constraints: Constraints): Placeable {
+        val placeable = super.performMeasure(constraints)
+        val invokeRemeasureCallbacks = {
+            modifier.onRemeasured(measuredSize)
+        }
+        layoutNode.owner?.snapshotObserver?.pauseSnapshotReadObservation(invokeRemeasureCallbacks)
+            ?: invokeRemeasureCallbacks.invoke()
+        return placeable
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RootForTest.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RootForTest.kt
new file mode 100644
index 0000000..cd72eb2
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RootForTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.node
+
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.semantics.SemanticsOwner
+import androidx.compose.ui.text.input.TextInputService
+import androidx.compose.ui.unit.Density
+
+/**
+ * The marker interface to be implemented by the root backing the composition.
+ * To be used in tests.
+ */
+interface RootForTest {
+    /**
+     * Current device density.
+     */
+    val density: Density
+
+    /**
+     * Semantics owner for this root. Manages all the semantics nodes.
+     */
+    val semanticsOwner: SemanticsOwner
+
+    /**
+     * The service handling text input.
+     */
+    val textInputService: TextInputService
+
+    /**
+     * Send this [KeyEvent] to the focused component in this [Owner].
+     *
+     * @return true if the event was consumed. False otherwise.
+     */
+    fun sendKeyEvent(keyEvent: KeyEvent): Boolean
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
index 7b5b0f3..87b4899b2 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
@@ -26,7 +26,7 @@
 import androidx.compose.ui.layout.globalPosition
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.node.LayoutNodeWrapper
-import androidx.compose.ui.node.Owner
+import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.fastForEach
 
@@ -73,10 +73,9 @@
     val layoutInfo: LayoutInfo = layoutNodeWrapper.layoutNode
 
     /**
-     * The [Owner] this node is attached to.
+     * The [root][RootForTest] this node is attached to.
      */
-    // TODO(b/174747742) Stop using Owner in tests and use RootForTest instead
-    val owner: Owner? get() = layoutNode.owner
+    val root: RootForTest? get() = layoutNode.owner?.rootForTest
 
     /**
      * The [LayoutNode] that this is associated with.
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
index 1a72190..8e9fbd0 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
@@ -46,6 +46,7 @@
 import androidx.compose.ui.node.MeasureAndLayoutDelegate
 import androidx.compose.ui.node.Owner
 import androidx.compose.ui.node.OwnerSnapshotObserver
+import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.semantics.SemanticsModifierCore
 import androidx.compose.ui.semantics.SemanticsOwner
 import androidx.compose.ui.text.input.TextInputService
@@ -64,7 +65,7 @@
 class DesktopOwner(
     val container: DesktopOwners,
     density: Density = Density(1f, 1f)
-) : Owner {
+) : Owner, RootForTest {
     internal var size by mutableStateOf(IntSize(0, 0))
 
     override var density by mutableStateOf(density)
@@ -97,6 +98,8 @@
             .then(keyInputModifier)
     }
 
+    override val rootForTest = this
+
     override val snapshotObserver = OwnerSnapshotObserver { command ->
         command()
     }
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 09dfdc4..b262eeb 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
@@ -30,7 +30,6 @@
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.hapticfeedback.HapticFeedback
-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.LayoutModifier
@@ -44,7 +43,6 @@
 import androidx.compose.ui.platform.TextToolbar
 import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.platform.WindowInfo
-import androidx.compose.ui.semantics.SemanticsOwner
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.input.TextInputService
 import androidx.compose.ui.unit.Constraints
@@ -1720,6 +1718,8 @@
     val >
     var layoutChangeCount = 0
 
+    override val rootForTest: RootForTest
+        get() = TODO("Not yet implemented")
     override val hapticFeedBack: HapticFeedback
         get() = TODO("Not yet implemented")
     override val clipboardManager: ClipboardManager
@@ -1734,8 +1734,6 @@
         get() = TODO("Not yet implemented")
     override val density: Density
         get() = Density(1f)
-    override val semanticsOwner: SemanticsOwner
-        get() = TODO("Not yet implemented")
     override val textInputService: TextInputService
         get() = TODO("Not yet implemented")
     override val focusManager: FocusManager
@@ -1771,8 +1769,6 @@
 
     override fun requestFocus(): Boolean = false
 
-    override fun sendKeyEvent(keyEvent: KeyEvent): Boolean = false
-
     override fun measureAndLayout() {
     }
 
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 8a6e871..072e510 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -420,6 +420,9 @@
 WARNING: Illegal reflective access by androidx\.room\.compiler\.processing\.javac\.JavacProcessingEnvMessager\$Companion\$isFromCompiledClass\$[0-9]+ \(file:\$OUT_DIR/androidx/room/room\-compiler\-processing/build/libs/room\-compiler\-processing\-[0-9]+\.[0-9]+\.[0-9]+\-alpha[0-9]+\.jar\) to field com\.sun\.tools\.javac\.code\.Symbol\$ClassSymbol\.classfile
 WARNING: Please consider reporting this to the maintainers of androidx\.room\.compiler\.processing\.javac\.JavacProcessingEnvMessager\$Companion\$isFromCompiledClass\$[0-9]+
 # > Task :wear:wear-watchface-complications-rendering:compileDebugUnitTestJavaWithJavac
+# > Task :wear:wear-watchface:testDebugUnitTest
+System\.logW\: A resource was acquired at attached stack trace but never released\. See java\.io\.Closeable for information on avoiding resource leaks\.java\.lang\.Throwable\: Explicit termination method \'dispose\' not called
+System\.logW\: A resource was acquired at attached stack trace but never released\. See java\.io\.Closeable for information on avoiding resource leaks\.java\.lang\.Throwable\: Explicit termination method \'release\' not called
 # > Task :benchmark:benchmark-perfetto:mergeDebugAndroidTestJavaResource
 More than one file was found with OS independent path '.*'\. This version of the Android Gradle Plugin chooses the file from the app or dynamic\-feature module, but this can cause unexpected behavior or errors at runtime\. Future versions of the Android Gradle Plugin will throw an error in this case\.
 # > Task :docs-runner:dokkaJavaTipOfTreeDocs
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index 146b6ca..e134e44 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -35,6 +35,7 @@
     docs(project(":camera:camera-lifecycle"))
     docs(project(":camera:camera-view"))
     docs(project(":car:app:app"))
+    docs(project(":car:app:app-aaos"))
     docs(project(":cardview:cardview"))
     docs(project(":collection:collection"))
     docs(project(":collection:collection-ktx"))
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java
index 04ce980..0008abd 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java
@@ -230,9 +230,9 @@
             Animation anim = Preconditions.checkNotNull(
                     Preconditions.checkNotNull(animationInfo.getAnimation(context)).animation);
             Operation.State finalState = operation.getFinalState();
-            if (finalState == Operation.State.VISIBLE) {
-                // If we've moving to VISIBLE, we can't use a AnimationSet
-                // due that causing the introduction of visual artifacts (b/163084315).
+            if (finalState != Operation.State.REMOVED) {
+                // If the operation does not remove the view, we can't use a
+                // AnimationSet due that causing the introduction of visual artifacts (b/163084315).
                 viewToAnimate.startAnimation(anim);
                 // This means we can't use setAnimationListener() without overriding
                 // any listener that the Fragment has set themselves, so we
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
index 8cccb82..e370f53 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
@@ -95,6 +95,10 @@
     private static final String SAVED_CANCELABLE = "android:cancelable";
     private static final String SAVED_SHOWS_DIALOG = "android:showsDialog";
     private static final String SAVED_BACK_STACK_ID = "android:backStackId";
+    /**
+     * Copied from {@link Dialog}.
+     */
+    private static final String SAVED_INTERNAL_DIALOG_SHOWING = "android:dialogShowing";
 
     private Handler mHandler;
     private Runnable mDismissRunnable = new Runnable() {
@@ -691,6 +695,7 @@
         super.onSaveInstanceState(outState);
         if (mDialog != null) {
             Bundle dialogState = mDialog.onSaveInstanceState();
+            dialogState.putBoolean(SAVED_INTERNAL_DIALOG_SHOWING, false);
             outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
         }
         if (mStyle != STYLE_NORMAL) {
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/GenerateProguardDetectionFileTask.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/GenerateProguardDetectionFileTask.kt
index ac34dbb..08cd184 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/GenerateProguardDetectionFileTask.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/GenerateProguardDetectionFileTask.kt
@@ -114,5 +114,7 @@
     val strippedArtifact = mavenArtifact.removePrefix(mavenGroup.split('.').last())
         .removePrefix("-").replace('-', '.')
     val group = mavenGroup.removePrefix("androidx.")
-    return "androidx.inspection.$group.$strippedArtifact"
+    // It's possible for strippedArtifact to be empty, e.g. "compose.ui/ui" has no hyphen
+    return "androidx.inspection.$group" +
+        if (strippedArtifact.isNotEmpty()) ".$strippedArtifact" else ""
 }
\ No newline at end of file
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
index e5a1eba..81df245 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
@@ -124,10 +124,12 @@
     inspectorProject.project.plugins.withType(InspectionPlugin::class.java) { inspectionPlugin ->
         val libExtension = libraryProject.extensions.getByType(LibraryExtension::class.java)
         libExtension.libraryVariants.all { variant ->
-            variant.packageLibraryProvider.configure { zip ->
-                val outputFile = inspectionPlugin.dexTask.get().outputFile
-                zip.from(outputFile)
-                zip.rename(outputFile.asFile.get().name, "inspector.jar")
+            inspectorProject.afterEvaluate {
+                variant.packageLibraryProvider.configure { zip ->
+                    val outputFile = inspectionPlugin.dexTask.get().outputFile
+                    zip.from(outputFile)
+                    zip.rename(outputFile.asFile.get().name, "inspector.jar")
+                }
             }
         }
     }
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.java b/navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.kt
similarity index 91%
rename from navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.java
rename to navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.kt
index f9f0fbb..167dcb1 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.java
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavArgs.kt
@@ -13,11 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package androidx.navigation;
+package androidx.navigation
 
 /**
  * An interface marking generated Args classes.
  */
-public interface NavArgs {
-}
+public interface NavArgs
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NoOpNavigator.java b/navigation/navigation-common/src/main/java/androidx/navigation/NoOpNavigator.java
deleted file mode 100644
index a4ed438..0000000
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NoOpNavigator.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.navigation;
-
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-/**
- * A {@link Navigator} that only supports creating destinations.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@Navigator.Name("NoOp")
-public class NoOpNavigator extends Navigator<NavDestination> {
-    @NonNull
-    @Override
-    public NavDestination createDestination() {
-        return new NavDestination(this);
-    }
-
-    @Nullable
-    @Override
-    public NavDestination navigate(@NonNull NavDestination destination, @Nullable Bundle args,
-            @Nullable NavOptions navOptions, @Nullable Extras navigatorExtras) {
-        return destination;
-    }
-
-    @Override
-    public boolean popBackStack() {
-        return true;
-    }
-}
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NoOpNavigator.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NoOpNavigator.kt
new file mode 100644
index 0000000..044db7a
--- /dev/null
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NoOpNavigator.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.navigation
+
+import android.os.Bundle
+import androidx.annotation.RestrictTo
+
+/**
+ * A [Navigator] that only supports creating destinations.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Navigator.Name("NoOp")
+public class NoOpNavigator : Navigator<NavDestination>() {
+    override fun createDestination(): NavDestination = NavDestination(this)
+
+    override fun navigate(
+        destination: NavDestination,
+        args: Bundle?,
+        navOptions: NavOptions?,
+        navigatorExtras: Extras?
+    ): NavDestination? = destination
+
+    override fun popBackStack(): Boolean = true
+}
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt
index 5367b9f..7c3f3159c 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt
@@ -53,7 +53,7 @@
                 val entryRoute = navBackStackEntry?.arguments?.getString(KEY_ROUTE)
                 items.forEach { (name, route) ->
                     BottomNavigationItem(
-                        icon = { Icon(Icons.Filled.Favorite) },
+                        icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
                         label = { Text(name) },
                         selected = entryRoute == route,
                         >
diff --git a/settings.gradle b/settings.gradle
index a7f2b13..cae4b3a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -159,6 +159,7 @@
 includeProject(":benchmark:integration-tests:startup-benchmark", "benchmark/integration-tests/startup-benchmark", [BuildType.MAIN])
 includeProject(":biometric:biometric", "biometric/biometric", [BuildType.MAIN])
 includeProject(":biometric:biometric-ktx", "biometric/biometric-ktx", [BuildType.MAIN])
+includeProject(":biometric:biometric-ktx-samples", "biometric/biometric-ktx/samples", [BuildType.MAIN])
 includeProject(":biometric:integration-tests:testapp", "biometric/integration-tests/testapp", [BuildType.MAIN])
 includeProject(":browser:browser", "browser/browser", [BuildType.MAIN])
 includeProject(":buildSrc-tests", "buildSrc-tests", [BuildType.MAIN])
@@ -186,6 +187,7 @@
 includeProject(":camera:integration-tests:camera-testapp-view", "camera/integration-tests/viewtestapp", [BuildType.MAIN])
 includeProject(":camera:integration-tests:camera-testlib-extensions", "camera/integration-tests/extensionstestlib", [BuildType.MAIN])
 includeProject(":car:app:app", "car/app/app", [BuildType.MAIN])
+includeProject(":car:app:app-aaos", "car/app/app-aaos", [BuildType.MAIN])
 includeProject(":cardview:cardview", "cardview/cardview", [BuildType.MAIN])
 includeProject(":collection:collection", "collection/collection", [BuildType.MAIN])
 includeProject(":collection:collection-benchmark", "collection/collection-benchmark", [BuildType.MAIN])
@@ -255,6 +257,7 @@
 includeProject(":compose:ui:ui-geometry", "compose/ui/ui-geometry", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-graphics", "compose/ui/ui-graphics", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-graphics:ui-graphics-samples", "compose/ui/ui-graphics/samples", [BuildType.COMPOSE])
+includeProject(":compose:ui:ui-inspection", "compose/ui/ui-inspection", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-lint", "compose/ui/ui-lint", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-test", "compose/ui/ui-test", [BuildType.COMPOSE])
 includeProject(":compose:ui:ui-test-font", "compose/ui/ui-test-font", [BuildType.COMPOSE])
@@ -326,7 +329,7 @@
 includeProject(":hilt:hilt-work", "hilt/hilt-work", [BuildType.MAIN])
 includeProject(":hilt:integration-tests:hilt-testapp-viewmodel", "hilt/integration-tests/viewmodelapp", [BuildType.MAIN])
 includeProject(":hilt:integration-tests:hilt-testapp-worker", "hilt/integration-tests/workerapp", [BuildType.MAIN])
-includeProject(":inspection:inspection", "inspection/inspection", [BuildType.MAIN])
+includeProject(":inspection:inspection", "inspection/inspection", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":inspection:inspection-gradle-plugin", "inspection/inspection-gradle-plugin", [BuildType.MAIN])
 includeProject(":inspection:inspection-testing", "inspection/inspection-testing", [BuildType.MAIN])
 includeProject(":interpolator:interpolator", "interpolator/interpolator", [BuildType.MAIN])
@@ -360,11 +363,11 @@
 includeProject(":lifecycle:lifecycle-runtime", "lifecycle/lifecycle-runtime", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:lifecycle-runtime-ktx", "lifecycle/lifecycle-runtime-ktx", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:lifecycle-runtime-ktx-lint", "lifecycle/lifecycle-runtime-ktx-lint", [BuildType.MAIN, BuildType.FLAN])
-includeProject(":lifecycle:lifecycle-runtime-testing", "lifecycle/lifecycle-runtime-testing", [BuildType.MAIN, BuildType.FLAN, BuildType.FLAN])
+includeProject(":lifecycle:lifecycle-runtime-testing", "lifecycle/lifecycle-runtime-testing", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:lifecycle-service", "lifecycle/lifecycle-service", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:lifecycle-viewmodel", "lifecycle/lifecycle-viewmodel", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:lifecycle-viewmodel-ktx", "lifecycle/lifecycle-viewmodel-ktx", [BuildType.MAIN, BuildType.FLAN])
-includeProject(":lifecycle:lifecycle-viewmodel-savedstate", "lifecycle/lifecycle-viewmodel-savedstate", [BuildType.MAIN, BuildType.FLAN, BuildType.FLAN])
+includeProject(":lifecycle:lifecycle-viewmodel-savedstate", "lifecycle/lifecycle-viewmodel-savedstate", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lint-checks", "lint-checks")
 includeProject(":lint-checks:tests", "lint-checks/tests", [BuildType.MAIN])
 includeProject(":lint-demos:lint-demo-appcompat", "lint-demos/lint-demo-appcompat", [BuildType.MAIN])
diff --git a/slices/view/build.gradle b/slices/view/build.gradle
index 5d8e730..29aa15a 100644
--- a/slices/view/build.gradle
+++ b/slices/view/build.gradle
@@ -27,7 +27,7 @@
 dependencies {
     implementation(project(":slice-core"))
     implementation project(":appcompat:appcompat")
-    implementation("androidx.recyclerview:recyclerview:1.1.0")
+    implementation("androidx.recyclerview:recyclerview:1.2.0-beta01")
     implementation("androidx.collection:collection:1.1.0")
     api("androidx.lifecycle:lifecycle-livedata-core:2.0.0")
 
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceContent.java b/slices/view/src/main/java/androidx/slice/widget/SliceContent.java
index 6061e64..6dc5983 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceContent.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceContent.java
@@ -256,7 +256,8 @@
         }
         if (actionItem == null) {
             Intent intent = new Intent();
-            PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 0);
+            PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 
+                PendingIntent.FLAG_IMMUTABLE);
             actionItem = new SliceItem(pi, null, FORMAT_ACTION, null, null);
         }
         if (shortcutAction != null && shortcutIcon != null && actionItem != null) {
diff --git a/viewpager2/integration-tests/testapp/build.gradle b/viewpager2/integration-tests/testapp/build.gradle
index 8eae4e5..bf00bec 100644
--- a/viewpager2/integration-tests/testapp/build.gradle
+++ b/viewpager2/integration-tests/testapp/build.gradle
@@ -31,7 +31,6 @@
     id("AndroidXPlugin")
     id("com.android.application")
     id("org.jetbrains.kotlin.android")
-    id('kotlin-android-extensions')
 }
 
 dependencies {
diff --git a/wear/wear-complications-data/src/androidTest/java/androidx/wear/complications/ComplicationHelperActivityTest.kt b/wear/wear-complications-data/src/androidTest/java/androidx/wear/complications/ComplicationHelperActivityTest.kt
index ed7adc78..fdb6534 100644
--- a/wear/wear-complications-data/src/androidTest/java/androidx/wear/complications/ComplicationHelperActivityTest.kt
+++ b/wear/wear-complications-data/src/androidTest/java/androidx/wear/complications/ComplicationHelperActivityTest.kt
@@ -16,38 +16,99 @@
 package androidx.wear.complications
 
 import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
 import androidx.test.core.app.ApplicationProvider
 import androidx.wear.complications.data.ComplicationType
-import com.google.common.truth.Truth
+import androidx.wear.complications.data.ComplicationType.LONG_TEXT
+import androidx.wear.complications.data.ComplicationType.MONOCHROMATIC_IMAGE
+import androidx.wear.complications.data.ComplicationType.SHORT_TEXT
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 public class ComplicationHelperActivityTest {
+
     @Test
-    public fun createProviderChooserHelperIntent() {
-        val complicationId = 1234
-        val watchFaceComponentName = ComponentName("test.package", "test.class")
-        val complicationTypes = listOf(ComplicationType.SHORT_TEXT, ComplicationType.LONG_TEXT)
-        val intent = ComplicationHelperActivity.createProviderChooserHelperIntent(
-            ApplicationProvider.getApplicationContext(),
-            watchFaceComponentName,
-            complicationId,
-            complicationTypes
-        )
-        val expectedSupportedTypes = intArrayOf(
-            ComplicationType.SHORT_TEXT.asWireComplicationType(),
-            ComplicationType.LONG_TEXT.asWireComplicationType()
-        )
-        val actualComponentName = intent.getParcelableExtra<ComponentName>(
-            ProviderChooserIntent.EXTRA_WATCH_FACE_COMPONENT_NAME
-        )
-        Truth.assertThat(actualComponentName).isEqualTo(watchFaceComponentName)
-        Truth.assertThat(intent.getIntExtra(ProviderChooserIntent.EXTRA_COMPLICATION_ID, -1))
-            .isEqualTo(complicationId)
-        Truth.assertThat(intent.getIntArrayExtra(ProviderChooserIntent.EXTRA_SUPPORTED_TYPES))
-            .isEqualTo(expectedSupportedTypes)
-        Truth.assertThat(intent.action)
+    public fun createProviderChooserHelperIntent_action() {
+        assertThat(createIntent().action)
             .isEqualTo(ComplicationHelperActivity.ACTION_START_PROVIDER_CHOOSER)
-        Truth.assertThat(intent.component!!.className)
-            .isEqualTo(ComplicationHelperActivity::class.java.name)
+    }
+
+    @Test
+    public fun createProviderChooserHelperIntent_component() {
+        assertThat(createIntent().component)
+            .isEqualTo(ComponentName(context, ComplicationHelperActivity::class.java))
+    }
+
+    @Test
+    public fun createProviderChooserHelperIntent_watchFaceComponentName() {
+        ComponentName("package-name", "watch-face-service-name").let {
+            assertThat(createIntent(watchFaceComponentName = it).watchFaceComponentName)
+                .isEqualTo(it)
+        }
+        ComponentName(context, "service-name").let {
+            assertThat(createIntent(watchFaceComponentName = it).watchFaceComponentName)
+                .isEqualTo(it)
+        }
+    }
+
+    @Test
+    public fun createProviderChooserHelperIntent_complicationId() {
+        assertThat(createIntent(complicationId = -1).complicationId).isEqualTo(-1)
+        assertThat(createIntent(complicationId = 1234).complicationId).isEqualTo(1234)
+        assertThat(createIntent(complicationId = 30000).complicationId).isEqualTo(30000)
+    }
+
+    @Test
+    public fun createProviderChooserHelperIntent_supportedTypes() {
+        arrayOf<ComplicationType>().let {
+            assertThat(createIntent(supportedTypes = it).supportedTypes).isEqualTo(it)
+        }
+        arrayOf(LONG_TEXT).let {
+            assertThat(createIntent(supportedTypes = it).supportedTypes).isEqualTo(it)
+        }
+        arrayOf(SHORT_TEXT, LONG_TEXT, MONOCHROMATIC_IMAGE).let {
+            assertThat(createIntent(supportedTypes = it).supportedTypes).isEqualTo(it)
+        }
+    }
+
+    /** Creates an intent with default values for unspecified parameters. */
+    private fun createIntent(
+        watchFaceComponentName: ComponentName = defaultWatchFaceComponentName,
+        complicationId: Int = defaultComplicationId,
+        vararg supportedTypes: ComplicationType = defaultSupportedTypes
+    ) = ComplicationHelperActivity.createProviderChooserHelperIntent(
+        context,
+        watchFaceComponentName,
+        complicationId,
+        supportedTypes.asList()
+    )
+
+    private companion object {
+        /** The context to be used in the various tests. */
+        private val context = ApplicationProvider.getApplicationContext<Context>()
+
+        /** The default watch face component name used in the test. */
+        private val defaultWatchFaceComponentName = ComponentName("test.package", "test.class")
+
+        /** The default complication ID used in the test. */
+        private const val defaultComplicationId = 1234
+
+        /** The default supported types used in the test. */
+        private val defaultSupportedTypes = arrayOf(SHORT_TEXT, LONG_TEXT)
     }
 }
+
+/** The watch face component name encoded in the intent. */
+private val Intent.watchFaceComponentName
+    get() = getParcelableExtra<ComponentName>(ProviderChooserIntent.EXTRA_WATCH_FACE_COMPONENT_NAME)
+
+/** The complication ID encoded in the intent. */
+private val Intent.complicationId
+    get() = getIntExtra(ProviderChooserIntent.EXTRA_COMPLICATION_ID, -1)
+
+/** The support types encoded in the intent. */
+private val Intent.supportedTypes
+    get() = ComplicationType.fromWireTypes(
+        getIntArrayExtra(ProviderChooserIntent.EXTRA_SUPPORTED_TYPES)!!
+    )
\ No newline at end of file
diff --git a/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java b/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java
index cdb4705..764f5cb 100644
--- a/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java
+++ b/wear/wear-watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java
@@ -51,7 +51,6 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.wear.complications.ComplicationHelperActivity;
 import androidx.wear.watchface.CanvasType;
-import androidx.wear.watchface.ComplicationsManager;
 import androidx.wear.watchface.Renderer;
 import androidx.wear.watchface.WatchFace;
 import androidx.wear.watchface.WatchFaceService;
@@ -724,7 +723,6 @@
             return new WatchFace(
                     WatchFaceType.ANALOG,
                     userStyleRepository,
-                    new ComplicationsManager(new ArrayList<>(), userStyleRepository),
                     new Renderer.CanvasRenderer(
                             surfaceHolder, userStyleRepository, watchState, CanvasType.SOFTWARE,
                             16L) {
diff --git a/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt b/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
index 7319806..99cc7f6 100644
--- a/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
+++ b/wear/wear-watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
@@ -371,12 +371,12 @@
             )
         }
 
-    override var userStyle: UserStyle
-        // We return a deep copy of the style because assigning to it can otherwise have unexpected
-        // side effects.
-        get() = UserStyle(editorDelegate.userStyleRepository.userStyle)
+    // We make a deep copy of the style because assigning to it can otherwise have unexpected
+    // side effects (it would apply to the active watch face).
+    override var userStyle = UserStyle(editorDelegate.userStyleRepository.userStyle)
         set(value) {
-            editorDelegate.userStyleRepository.userStyle = value
+            field = value
+            editorDelegate.userStyleRepository.userStyle = UserStyle(value)
         }
 
     override fun takeWatchFaceScreenshot(
diff --git a/wear/wear-watchface/api/current.txt b/wear/wear-watchface/api/current.txt
index 5592cb3..b9bf6f7 100644
--- a/wear/wear-watchface/api/current.txt
+++ b/wear/wear-watchface/api/current.txt
@@ -37,6 +37,7 @@
     method public static androidx.wear.watchface.Complication.Builder createRoundRectComplicationBuilder(int id, androidx.wear.watchface.CanvasComplication renderer, java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy, androidx.wear.complications.ComplicationBounds complicationBounds);
     method public int getBoundsType();
     method @UiThread public androidx.wear.complications.ComplicationBounds getComplicationBounds();
+    method public android.os.Bundle? getComplicationConfigExtras();
     method public androidx.wear.watchface.ObservableWatchData<androidx.wear.complications.data.ComplicationData> getComplicationData();
     method @UiThread public androidx.wear.complications.DefaultComplicationProviderPolicy getDefaultProviderPolicy();
     method @UiThread public androidx.wear.complications.data.ComplicationType getDefaultProviderType();
@@ -50,6 +51,7 @@
     method @UiThread public void setRenderer(androidx.wear.watchface.CanvasComplication value);
     property public final int boundsType;
     property @UiThread public final androidx.wear.complications.ComplicationBounds complicationBounds;
+    property public final android.os.Bundle? complicationConfigExtras;
     property public final androidx.wear.watchface.ObservableWatchData<androidx.wear.complications.data.ComplicationData> complicationData;
     property @UiThread public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
     property @UiThread public final androidx.wear.complications.data.ComplicationType defaultProviderType;
@@ -61,6 +63,7 @@
 
   public static final class Complication.Builder {
     method public androidx.wear.watchface.Complication build();
+    method public androidx.wear.watchface.Complication.Builder setComplicationConfigExtras(android.os.Bundle? extras);
     method public androidx.wear.watchface.Complication.Builder setDefaultProviderType(androidx.wear.complications.data.ComplicationType defaultProviderType);
     method public androidx.wear.watchface.Complication.Builder setEnabled(boolean enabled);
   }
@@ -200,7 +203,8 @@
   }
 
   public final class WatchFace {
-    ctor public WatchFace(int watchFaceType, androidx.wear.watchface.style.UserStyleRepository userStyleRepository, androidx.wear.watchface.ComplicationsManager complicationsManager, androidx.wear.watchface.Renderer renderer);
+    ctor public WatchFace(int watchFaceType, androidx.wear.watchface.style.UserStyleRepository userStyleRepository, androidx.wear.watchface.Renderer renderer, androidx.wear.watchface.ComplicationsManager complicationsManager);
+    ctor public WatchFace(int watchFaceType, androidx.wear.watchface.style.UserStyleRepository userStyleRepository, androidx.wear.watchface.Renderer renderer);
     method public androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle getLegacyWatchFaceStyle();
     method public Long? getOverridePreviewReferenceTimeMillis();
     method public androidx.wear.watchface.style.UserStyleRepository getUserStyleRepository();
diff --git a/wear/wear-watchface/api/public_plus_experimental_current.txt b/wear/wear-watchface/api/public_plus_experimental_current.txt
index 5592cb3..b9bf6f7 100644
--- a/wear/wear-watchface/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface/api/public_plus_experimental_current.txt
@@ -37,6 +37,7 @@
     method public static androidx.wear.watchface.Complication.Builder createRoundRectComplicationBuilder(int id, androidx.wear.watchface.CanvasComplication renderer, java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy, androidx.wear.complications.ComplicationBounds complicationBounds);
     method public int getBoundsType();
     method @UiThread public androidx.wear.complications.ComplicationBounds getComplicationBounds();
+    method public android.os.Bundle? getComplicationConfigExtras();
     method public androidx.wear.watchface.ObservableWatchData<androidx.wear.complications.data.ComplicationData> getComplicationData();
     method @UiThread public androidx.wear.complications.DefaultComplicationProviderPolicy getDefaultProviderPolicy();
     method @UiThread public androidx.wear.complications.data.ComplicationType getDefaultProviderType();
@@ -50,6 +51,7 @@
     method @UiThread public void setRenderer(androidx.wear.watchface.CanvasComplication value);
     property public final int boundsType;
     property @UiThread public final androidx.wear.complications.ComplicationBounds complicationBounds;
+    property public final android.os.Bundle? complicationConfigExtras;
     property public final androidx.wear.watchface.ObservableWatchData<androidx.wear.complications.data.ComplicationData> complicationData;
     property @UiThread public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
     property @UiThread public final androidx.wear.complications.data.ComplicationType defaultProviderType;
@@ -61,6 +63,7 @@
 
   public static final class Complication.Builder {
     method public androidx.wear.watchface.Complication build();
+    method public androidx.wear.watchface.Complication.Builder setComplicationConfigExtras(android.os.Bundle? extras);
     method public androidx.wear.watchface.Complication.Builder setDefaultProviderType(androidx.wear.complications.data.ComplicationType defaultProviderType);
     method public androidx.wear.watchface.Complication.Builder setEnabled(boolean enabled);
   }
@@ -200,7 +203,8 @@
   }
 
   public final class WatchFace {
-    ctor public WatchFace(int watchFaceType, androidx.wear.watchface.style.UserStyleRepository userStyleRepository, androidx.wear.watchface.ComplicationsManager complicationsManager, androidx.wear.watchface.Renderer renderer);
+    ctor public WatchFace(int watchFaceType, androidx.wear.watchface.style.UserStyleRepository userStyleRepository, androidx.wear.watchface.Renderer renderer, androidx.wear.watchface.ComplicationsManager complicationsManager);
+    ctor public WatchFace(int watchFaceType, androidx.wear.watchface.style.UserStyleRepository userStyleRepository, androidx.wear.watchface.Renderer renderer);
     method public androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle getLegacyWatchFaceStyle();
     method public Long? getOverridePreviewReferenceTimeMillis();
     method public androidx.wear.watchface.style.UserStyleRepository getUserStyleRepository();
diff --git a/wear/wear-watchface/api/restricted_current.txt b/wear/wear-watchface/api/restricted_current.txt
index 505f3b1..f43ccbf 100644
--- a/wear/wear-watchface/api/restricted_current.txt
+++ b/wear/wear-watchface/api/restricted_current.txt
@@ -37,6 +37,7 @@
     method public static androidx.wear.watchface.Complication.Builder createRoundRectComplicationBuilder(int id, androidx.wear.watchface.CanvasComplication renderer, java.util.List<? extends androidx.wear.complications.data.ComplicationType> supportedTypes, androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy, androidx.wear.complications.ComplicationBounds complicationBounds);
     method public int getBoundsType();
     method @UiThread public androidx.wear.complications.ComplicationBounds getComplicationBounds();
+    method public android.os.Bundle? getComplicationConfigExtras();
     method public androidx.wear.watchface.ObservableWatchData<androidx.wear.complications.data.ComplicationData> getComplicationData();
     method @UiThread public androidx.wear.complications.DefaultComplicationProviderPolicy getDefaultProviderPolicy();
     method @UiThread public androidx.wear.complications.data.ComplicationType getDefaultProviderType();
@@ -50,6 +51,7 @@
     method @UiThread public void setRenderer(androidx.wear.watchface.CanvasComplication value);
     property public final int boundsType;
     property @UiThread public final androidx.wear.complications.ComplicationBounds complicationBounds;
+    property public final android.os.Bundle? complicationConfigExtras;
     property public final androidx.wear.watchface.ObservableWatchData<androidx.wear.complications.data.ComplicationData> complicationData;
     property @UiThread public final androidx.wear.complications.DefaultComplicationProviderPolicy defaultProviderPolicy;
     property @UiThread public final androidx.wear.complications.data.ComplicationType defaultProviderType;
@@ -61,6 +63,7 @@
 
   public static final class Complication.Builder {
     method public androidx.wear.watchface.Complication build();
+    method public androidx.wear.watchface.Complication.Builder setComplicationConfigExtras(android.os.Bundle? extras);
     method public androidx.wear.watchface.Complication.Builder setDefaultProviderType(androidx.wear.complications.data.ComplicationType defaultProviderType);
     method public androidx.wear.watchface.Complication.Builder setEnabled(boolean enabled);
   }
@@ -230,7 +233,8 @@
   }
 
   public final class WatchFace {
-    ctor public WatchFace(int watchFaceType, androidx.wear.watchface.style.UserStyleRepository userStyleRepository, androidx.wear.watchface.ComplicationsManager complicationsManager, androidx.wear.watchface.Renderer renderer);
+    ctor public WatchFace(int watchFaceType, androidx.wear.watchface.style.UserStyleRepository userStyleRepository, androidx.wear.watchface.Renderer renderer, androidx.wear.watchface.ComplicationsManager complicationsManager);
+    ctor public WatchFace(int watchFaceType, androidx.wear.watchface.style.UserStyleRepository userStyleRepository, androidx.wear.watchface.Renderer renderer);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.wear.watchface.WatchFace.EditorDelegate? getEditorDelegate(android.content.ComponentName componentName);
     method public androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle getLegacyWatchFaceStyle();
     method public Long? getOverridePreviewReferenceTimeMillis();
@@ -292,9 +296,7 @@
     method public androidx.wear.watchface.style.data.UserStyleWireFormat? getInitialUserStyle();
     method @UiThread public void invalidate();
     method public void setActiveComplications(int[] watchFaceComplicationIds);
-    method public void setComplicationDetails(int complicationId, android.graphics.Rect bounds, @androidx.wear.watchface.data.ComplicationBoundsType int boundsType, int[] types);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void setContentDescriptionLabels(android.support.wearable.watchface.accessibility.ContentDescriptionLabel![] labels);
-    method public void setCurrentUserStyle(androidx.wear.watchface.style.data.UserStyleWireFormat userStyle);
     method public void setDefaultComplicationProviderWithFallbacks(int watchFaceComplicationId, java.util.List<android.content.ComponentName>? providers, @androidx.wear.complications.SystemProviders.ProviderId int fallbackSystemProvider, int type);
   }
 
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
index 1324b1f..19e14a1 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
@@ -262,8 +262,8 @@
     return WatchFace(
         WatchFaceType.ANALOG,
         userStyleRepository,
-        complicationsManager,
-        renderer
+        renderer,
+        complicationsManager
     )
 }
 
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt
index cedac02..bd084a8 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt
@@ -623,8 +623,8 @@
         return WatchFace(
             WatchFaceType.ANALOG,
             userStyleRepository,
-            complicationsManager,
-            renderer
+            renderer,
+            complicationsManager
         )
     }
 }
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 6861812..d5ec784 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
@@ -139,8 +139,8 @@
     return WatchFace(
         WatchFaceType.ANALOG,
         userStyleRepository,
-        complicationsManager,
-        renderer
+        renderer,
+        complicationsManager
     ).setLegacyWatchFaceStyle(
         WatchFace.LegacyWatchFaceOverlayStyle(
             0,
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
index ab10e84..7b4bd98d 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
@@ -171,8 +171,8 @@
             return WatchFace(
                 WatchFaceType.ANALOG,
                 userStyleRepository,
-                complicationSlots,
-                renderer
+                renderer,
+                complicationSlots
             )
         }
     }
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 06a5fa1..1922e79 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
@@ -22,6 +22,7 @@
 import android.graphics.RectF
 import android.graphics.drawable.Drawable
 import android.icu.util.Calendar
+import android.os.Bundle
 import android.support.wearable.complications.ComplicationData
 import androidx.annotation.ColorInt
 import androidx.annotation.UiThread
@@ -34,6 +35,7 @@
 import androidx.wear.watchface.data.ComplicationBoundsType
 import androidx.wear.watchface.style.Layer
 import androidx.wear.watchface.style.UserStyleSetting
+import androidx.wear.watchface.style.UserStyleSetting.ComplicationsUserStyleSetting
 
 /** Interface for rendering complications onto a [Canvas]. */
 public interface CanvasComplication {
@@ -247,7 +249,10 @@
      * The initial state of the complication. Note complications can be enabled / disabled by
      * [UserStyleSetting.ComplicationsUserStyleSetting].
      */
-    initiallyEnabled: Boolean
+    initiallyEnabled: Boolean,
+
+    /** Extras to be merged into the Intent sent when invoking the provider chooser activity. */
+    public val complicationConfigExtras: Bundle?
 ) {
     public companion object {
         internal val unitSquare = RectF(0f, 0f, 1f, 1f)
@@ -350,6 +355,7 @@
     ) {
         private var defaultProviderType = ComplicationType.NOT_CONFIGURED
         private var initiallyEnabled = true
+        private var complicationConfigExtras: Bundle? = null
 
         /**
          * Sets the initial [ComplicationType] to use with the initial complication provider.
@@ -363,11 +369,24 @@
             return this
         }
 
+        /**
+         * Whether the complication is initially enabled or not (by default its enabled). This can
+         * be overridden by [ComplicationsUserStyleSetting].
+         */
         public fun setEnabled(enabled: Boolean): Builder {
             this.initiallyEnabled = enabled
             return this
         }
 
+        /**
+         * Sets optional extras to be merged into the Intent sent when invoking the provider chooser
+         * activity.
+         */
+        public fun setComplicationConfigExtras(extras: Bundle?): Builder {
+            this.complicationConfigExtras = extras
+            return this
+        }
+
         /** Constructs the [Complication]. */
         public fun build(): Complication = Complication(
             id,
@@ -377,7 +396,8 @@
             supportedTypes,
             defaultProviderPolicy,
             defaultProviderType,
-            initiallyEnabled
+            initiallyEnabled,
+            complicationConfigExtras
         )
     }
 
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 c895684..93b37af 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
@@ -21,6 +21,7 @@
 import android.content.Context
 import android.content.Intent
 import android.icu.util.Calendar
+import android.os.Bundle
 import android.support.wearable.watchface.accessibility.AccessibilityUtils
 import android.support.wearable.watchface.accessibility.ContentDescriptionLabel
 import androidx.annotation.UiThread
@@ -279,12 +280,14 @@
      * Called when new complication data is received.
      *
      * @param watchFaceComplicationId The id of the complication that the data relates to. This
-     *     will be an id that was previously sent in a call to [setActiveComplications].
+     *     will be an id that was previously sent in a call to [setActiveComplications]. If this id
+     *     is unrecognized the call will be a NOP, the only circumstance when that happens is if
+     *     the watch face changes it's complication config between runs e.g. during development.
      * @param data The [ComplicationData] that should be displayed in the complication.
      */
     @UiThread
     internal fun onComplicationDataUpdate(watchFaceComplicationId: Int, data: ComplicationData) {
-        val complication = complications[watchFaceComplicationId]!!
+        val complication = complications[watchFaceComplicationId] ?: return
         complication.dataDirty =
             complication.dataDirty || (complication.renderer.idAndData?.complicationData != data)
         complication.renderer.idAndData = IdAndComplicationData(watchFaceComplicationId, data)
@@ -394,7 +397,11 @@
                 getComponentName(watchFaceHostApi.getContext()),
                 complicationId,
                 complication.supportedTypes
-            ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).apply {
+                complication.complicationConfigExtras?.let {
+                    replaceExtras(Bundle(it).apply { putAll(extras!!) })
+                }
+            }
         )
         for (complicationListener in complicationListeners) {
             complicationListener.onComplicationDoubleTapped(complicationId)
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
index 9c62c0d..d2a98e8 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
@@ -105,7 +105,7 @@
      * rates can also help preserve battery life, e.g. if a watch face has a short animation once
      * per second it can adjust the frame rate inorder to sleep when not animating.
      */
-    @IntRange(from = 0, to = 10000)
+    @IntRange(from = 0, to = 60000)
     public var interactiveDrawModeUpdateDelayMillis: Long,
 ) {
     internal lateinit var watchFaceHostApi: WatchFaceHostApi
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 f5a9ac1..0ab75a3 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
@@ -113,7 +113,7 @@
  * The return value of [WatchFaceService.createWatchFace] which brings together rendering, styling,
  * complications and state observers.
  */
-public class WatchFace(
+public class WatchFace @JvmOverloads constructor(
     /**
      * The type of watch face, whether it's digital or analog. Used to determine the
      * default time for editor preview screenshots.
@@ -123,11 +123,12 @@
     /** The [UserStyleRepository] for this WatchFace. */
     public val userStyleRepository: UserStyleRepository,
 
-    /** The [ComplicationsManager] for this WatchFace. */
-    internal var complicationsManager: ComplicationsManager,
-
     /** The [Renderer] for this WatchFace. */
-    internal val renderer: Renderer
+    internal val renderer: Renderer,
+
+    /** The [ComplicationsManager] for this WatchFace. */
+    internal var complicationsManager: ComplicationsManager =
+        ComplicationsManager(emptyList(), userStyleRepository)
 ) {
     internal var tapListener: TapListener? = null
 
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 b2e1896..dad0402 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
@@ -18,14 +18,12 @@
 
 import android.content.ComponentName
 import android.content.Context
-import android.graphics.Rect
 import android.os.Handler
 import android.support.wearable.complications.ComplicationData
 import android.support.wearable.watchface.accessibility.ContentDescriptionLabel
 import androidx.annotation.RestrictTo
 import androidx.annotation.UiThread
 import androidx.wear.complications.SystemProviders
-import androidx.wear.watchface.data.ComplicationBoundsType
 import androidx.wear.watchface.style.data.UserStyleWireFormat
 
 /**
@@ -40,20 +38,9 @@
     /** Returns the main thread [Handler]. */
     public fun getHandler(): Handler
 
-    /** Registers the watch face's current user style with the system. */
-    public fun setCurrentUserStyle(userStyle: UserStyleWireFormat)
-
     /** Returns the initial user style stored by the system if there is one or null otherwise. */
     public fun getInitialUserStyle(): UserStyleWireFormat?
 
-    /** Registers details of the complications with the system. */
-    public fun setComplicationDetails(
-        complicationId: Int,
-        bounds: Rect,
-        @ComplicationBoundsType boundsType: Int,
-        types: IntArray
-    )
-
     /**
      * Sets ContentDescriptionLabels for text-to-speech screen readers to make your
      * complications, buttons, and any other text on your watchface accessible.
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index bb0f91f..d497ee6 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -60,7 +60,6 @@
 import androidx.wear.watchface.control.data.HeadlessWatchFaceInstanceParams
 import androidx.wear.watchface.control.data.WallpaperInteractiveWatchFaceInstanceParams
 import androidx.wear.watchface.control.data.WatchfaceScreenshotParams
-import androidx.wear.watchface.data.ComplicationBoundsType
 import androidx.wear.watchface.data.ComplicationStateWireFormat
 import androidx.wear.watchface.data.DeviceConfig
 import androidx.wear.watchface.data.IdAndComplicationDataWireFormat
@@ -299,7 +298,7 @@
 
         private val invalidateRunnable = Runnable(this::invalidate)
 
-        // TODO(alexclarke): Figure out if we can remove this.
+        // State to support the old WSL style initialzation flow.
         private var pendingBackgroundAction: Bundle? = null
         private var pendingProperties: Bundle? = null
         private var pendingSetWatchFaceStyle = false
@@ -1075,19 +1074,6 @@
                 Log.e(TAG, "Failed to set accessibility labels: ", e)
             }
         }
-
-        override fun setCurrentUserStyle(userStyle: UserStyleWireFormat) {
-            // TODO(alexclarke): Report programmatic style changes to WCS.
-        }
-
-        override fun setComplicationDetails(
-            complicationId: Int,
-            bounds: Rect,
-            @ComplicationBoundsType boundsType: Int,
-            types: IntArray
-        ) {
-            // TODO(alexclarke): Report programmatic complication details changes to WCS.
-        }
     }
 }
 
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 7b3158c..ae550c7 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
@@ -95,8 +95,8 @@
     ) = WatchFace(
         watchFaceType,
         userStyleRepository,
-        complicationsManager,
-        renderer
+        renderer,
+        complicationsManager
     ).setSystemTimeProvider(object : WatchFace.SystemTimeProvider {
         override fun getSystemTimeMillis(): Long {
             return mockSystemTimeMillis
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index 5520c4a..2805d19 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -1884,6 +1884,39 @@
     }
 
     @Test
+    fun updateInvalidCOmpliationIdDoesNotCrash() {
+        initWallpaperInteractiveWatchFaceInstance(
+            WatchFaceType.ANALOG,
+            listOf(leftComplication),
+            UserStyleSchema(emptyList()),
+            WallpaperInteractiveWatchFaceInstanceParams(
+                "interactiveInstanceId",
+                DeviceConfig(
+                    false,
+                    false,
+                    0,
+                    0
+                ),
+                SystemState(false, 0),
+                UserStyle(emptyMap()).toWireFormat(),
+                null
+            )
+        )
+
+        // Send a complication with an invalid id - this should get ignored.
+        interactiveWatchFaceInstanceWCS.updateComplicationData(
+            listOf(
+                IdAndComplicationDataWireFormat(
+                    RIGHT_COMPLICATION_ID,
+                    ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
+                        .setShortText(ComplicationText.plainText("TYPE_SHORT_TEXT"))
+                        .build()
+                )
+            )
+        )
+    }
+
+    @Test
     fun invalidateRendererBeforeFullInit() {
         renderer = TestRenderer(
             surfaceHolder,
diff --git a/wear/wear/src/main/java/androidx/wear/widget/DismissibleFrameLayout.java b/wear/wear/src/main/java/androidx/wear/widget/DismissibleFrameLayout.java
index 448df50..3706add 100644
--- a/wear/wear/src/main/java/androidx/wear/widget/DismissibleFrameLayout.java
+++ b/wear/wear/src/main/java/androidx/wear/widget/DismissibleFrameLayout.java
@@ -142,7 +142,7 @@
         mContext = context;
 
         setSwipeDismissible(WearableNavigationHelper.isSwipeToDismissEnabled(context));
-        setBackButtonDismissible(true);
+        setBackButtonDismissible(false);
     }
 
     /** Registers a callback for dismissal. */
diff --git a/webkit/webkit/src/androidTest/java/androidx/webkit/ProxyControllerTest.java b/webkit/webkit/src/androidTest/java/androidx/webkit/ProxyControllerTest.java
index 571b9c4..c5009bf 100644
--- a/webkit/webkit/src/androidTest/java/androidx/webkit/ProxyControllerTest.java
+++ b/webkit/webkit/src/androidTest/java/androidx/webkit/ProxyControllerTest.java
@@ -147,6 +147,31 @@
 
     /**
      * This test should have an equivalent in CTS when this is implemented in the framework.
+     */
+    @Test
+    public void testReverseBypass() throws Exception {
+        WebkitUtils.checkFeature(WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS);
+
+        final String contentUrl = "http://www.example.com";
+        final String bypassUrl = "www.example.com";
+        int proxyServerRequestCount = mProxyServer.getRequestCount();
+
+        // Set proxy override with reverse bypass and load content url
+        // The content url (in the bypass list) should use proxy settings.
+        setProxyOverrideSync(new ProxyConfig.Builder()
+                .addProxyRule(mProxyServer.getHostName() + ":" + mProxyServer.getPort())
+                .addBypassRule(bypassUrl)
+                .setReverseBypass(true)
+                .build());
+        mWebViewOnUiThread.loadUrl(contentUrl);
+
+        proxyServerRequestCount++;
+        assertNotNull(mProxyServer.takeRequest(WebkitUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(proxyServerRequestCount, mProxyServer.getRequestCount());
+    }
+
+    /**
+     * This test should have an equivalent in CTS when this is implemented in the framework.
      *
      * Enumerates valid patterns to check they are supported.
      */
diff --git a/webkit/webkit/src/main/java/androidx/webkit/ProxyConfig.java b/webkit/webkit/src/main/java/androidx/webkit/ProxyConfig.java
index 1b62fc9..3fc5e78 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/ProxyConfig.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/ProxyConfig.java
@@ -17,6 +17,7 @@
 package androidx.webkit;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresFeature;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.StringDef;
 
@@ -68,14 +69,17 @@
 
     private List<ProxyRule> mProxyRules;
     private List<String> mBypassRules;
+    private boolean mReverseBypass;
 
     /**
      * @hide Internal use only
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public ProxyConfig(@NonNull List<ProxyRule> proxyRules, @NonNull List<String> bypassRules) {
+    public ProxyConfig(@NonNull List<ProxyRule> proxyRules, @NonNull List<String> bypassRules,
+            boolean reverseBypass) {
         mProxyRules = proxyRules;
         mBypassRules = bypassRules;
+        mReverseBypass = reverseBypass;
     }
 
     /**
@@ -106,6 +110,17 @@
     }
 
     /**
+     * @return reverseBypass
+     *
+     * TODO(laisminchillo): unhide this when we're ready to expose this
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public boolean isReverseBypass() {
+        return mReverseBypass;
+    }
+
+    /**
      * Class that holds a scheme filter and a proxy URL.
      */
     public static final class ProxyRule {
@@ -162,6 +177,7 @@
     public static final class Builder {
         private List<ProxyRule> mProxyRules;
         private List<String> mBypassRules;
+        private boolean mReverseBypass = false;
 
         /**
          * Create an empty ProxyConfig Builder.
@@ -177,6 +193,7 @@
         public Builder(@NonNull ProxyConfig proxyConfig) {
             mProxyRules = proxyConfig.getProxyRules();
             mBypassRules = proxyConfig.getBypassRules();
+            mReverseBypass = proxyConfig.isReverseBypass();
         }
 
         /**
@@ -186,7 +203,7 @@
          */
         @NonNull
         public ProxyConfig build() {
-            return new ProxyConfig(proxyRules(), bypassRules());
+            return new ProxyConfig(proxyRules(), bypassRules(), reverseBypass());
         }
 
         /**
@@ -316,6 +333,28 @@
             return addBypassRule(BYPASS_RULE_REMOVE_IMPLICIT);
         }
 
+        /**
+         * Reverse the bypass list, so only URLs in the bypass list will use these proxy settings.
+         *
+         * <p>
+         * This method should only be called if
+         * {@link WebViewFeature#isFeatureSupported(String)}
+         * returns {@code true} for {@link WebViewFeature#PROXY_OVERRIDE_REVERSE_BYPASS}.
+         *
+         * @return This Builder object
+         *
+         * TODO(laisminchillo): unhide this when we're ready to expose this
+         * @hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @RequiresFeature(name = WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS,
+                enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
+        @NonNull
+        public Builder setReverseBypass(boolean reverseBypass) {
+            mReverseBypass = reverseBypass;
+            return this;
+        }
+
         @NonNull
         private List<ProxyRule> proxyRules() {
             return mProxyRules;
@@ -325,5 +364,9 @@
         private List<String> bypassRules() {
             return mBypassRules;
         }
+
+        private boolean reverseBypass() {
+            return mReverseBypass;
+        }
     }
 }
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index 55916b9..1bb99540 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -93,6 +93,7 @@
             FORCE_DARK_STRATEGY,
             WEB_MESSAGE_LISTENER,
             DOCUMENT_START_SCRIPT,
+            PROXY_OVERRIDE_REVERSE_BYPASS,
     })
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.PARAMETER, ElementType.METHOD})
@@ -454,6 +455,16 @@
     public static final String DOCUMENT_START_SCRIPT = "DOCUMENT_START_SCRIPT";
 
     /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers {@link androidx.webkit.ProxyConfig.Builder.setReverseBypass(boolean)}
+     *
+     * TODO(laisminchillo): unhide when ready.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
+
+    /**
      * Return whether a feature is supported at run-time. On devices running Android version {@link
      * android.os.Build.VERSION_CODES#LOLLIPOP} and higher, this will check whether a feature is
      * supported, depending on the combination of the desired feature, the Android version of
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/ProxyControllerImpl.java b/webkit/webkit/src/main/java/androidx/webkit/internal/ProxyControllerImpl.java
index 36b5074a..5d7174f 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/ProxyControllerImpl.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/ProxyControllerImpl.java
@@ -36,13 +36,23 @@
     @Override
     public void setProxyOverride(@NonNull ProxyConfig proxyConfig, @NonNull Executor executor,
             @NonNull Runnable listener) {
-        WebViewFeatureInternal feature = WebViewFeatureInternal.PROXY_OVERRIDE;
-        if (feature.isSupportedByWebView()) {
-            // A 2D String array representation is required by reflection
-            String[][] proxyRuleArray = proxyRulesToStringArray(proxyConfig.getProxyRules());
-            String[] bypassRuleArray = proxyConfig.getBypassRules().toArray(new String[0]);
+        WebViewFeatureInternal proxyOverride = WebViewFeatureInternal.PROXY_OVERRIDE;
+        WebViewFeatureInternal reverseBypass = WebViewFeatureInternal.PROXY_OVERRIDE_REVERSE_BYPASS;
+
+        // A 2D String array representation is required by reflection
+        String[][] proxyRuleArray = proxyRulesToStringArray(proxyConfig.getProxyRules());
+        String[] bypassRuleArray = proxyConfig.getBypassRules().toArray(new String[0]);
+
+        if (proxyOverride.isSupportedByWebView() && !proxyConfig.isReverseBypass()) {
             getBoundaryInterface().setProxyOverride(
                     proxyRuleArray, bypassRuleArray, listener, executor);
+        } else if (proxyOverride.isSupportedByWebView() && reverseBypass.isSupportedByWebView()) {
+            getBoundaryInterface().setProxyOverride(
+                    proxyRuleArray,
+                    bypassRuleArray,
+                    listener,
+                    executor,
+                    proxyConfig.isReverseBypass());
         } else {
             throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
index 2bec18d..7ff7120 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
@@ -402,6 +402,12 @@
      */
     DOCUMENT_START_SCRIPT(WebViewFeature.DOCUMENT_START_SCRIPT, Features.DOCUMENT_START_SCRIPT),
 
+    /**
+     * This feature covers {@link androidx.webkit.ProxyConfig.Builder.setReverseBypass(boolean)}
+     */
+    PROXY_OVERRIDE_REVERSE_BYPASS(WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS,
+            Features.PROXY_OVERRIDE_REVERSE_BYPASS),
+
     ;  // This semicolon ends the enum. Add new features with a trailing comma above this line.
 
     private static final int NOT_SUPPORTED_BY_FRAMEWORK = -1;